summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc944
1 files changed, 59 insertions, 885 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f0c8819a52..700e1adf91 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -48,6 +48,7 @@
#include "leb128.h"
#include "oat.h"
#include "oat_file.h"
+#include "oat_file_assistant.h"
#include "object_lock.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
@@ -82,8 +83,7 @@ static void ThrowNoClassDefFoundError(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
Thread* self = Thread::Current();
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args);
+ self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args);
va_end(args);
}
@@ -105,14 +105,13 @@ static void ThrowEarlierClassFailure(mirror::Class* c)
mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
} else {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
if (c->GetVerifyErrorClass() != NULL) {
// TODO: change the verifier to store an _instance_, with a useful detail message?
std::string temp;
- self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
+ self->ThrowNewException(c->GetVerifyErrorClass()->GetDescriptor(&temp),
PrettyDescriptor(c).c_str());
} else {
- self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
+ self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
PrettyDescriptor(c).c_str());
}
}
@@ -141,9 +140,7 @@ static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
// We only wrap non-Error exceptions; an Error can just be used as-is.
if (!is_error) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;",
- nullptr);
+ self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
}
VlogClassInitializationFailure(klass);
}
@@ -662,77 +659,6 @@ void ClassLinker::RunRootClinits() {
}
}
-bool ClassLinker::GenerateOatFile(const char* dex_filename,
- int oat_fd,
- const char* oat_cache_filename,
- std::string* error_msg) {
- Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
- std::string dex2oat(Runtime::Current()->GetCompilerExecutable());
-
- gc::Heap* heap = Runtime::Current()->GetHeap();
- std::string boot_image_option("--boot-image=");
- if (heap->GetImageSpace() == nullptr) {
- // TODO If we get a dex2dex compiler working we could maybe use that, OTOH since we are likely
- // out of space anyway it might not matter.
- *error_msg = StringPrintf("Cannot create oat file for '%s' because we are running "
- "without an image.", dex_filename);
- return false;
- }
- boot_image_option += heap->GetImageSpace()->GetImageLocation();
-
- std::string dex_file_option("--dex-file=");
- dex_file_option += dex_filename;
-
- std::string oat_fd_option("--oat-fd=");
- StringAppendF(&oat_fd_option, "%d", oat_fd);
-
- std::string oat_location_option("--oat-location=");
- oat_location_option += oat_cache_filename;
-
- std::vector<std::string> argv;
- argv.push_back(dex2oat);
- argv.push_back("--runtime-arg");
- argv.push_back("-classpath");
- argv.push_back("--runtime-arg");
- argv.push_back(Runtime::Current()->GetClassPathString());
-
- Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
-
- if (!Runtime::Current()->IsVerificationEnabled()) {
- argv.push_back("--compiler-filter=verify-none");
- }
-
- if (Runtime::Current()->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_option);
- argv.push_back(dex_file_option);
- argv.push_back(oat_fd_option);
- argv.push_back(oat_location_option);
- const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
- for (size_t i = 0; i < compiler_options.size(); ++i) {
- argv.push_back(compiler_options[i].c_str());
- }
-
- if (!Exec(argv, error_msg)) {
- // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
- // died. Ignore unlink failure, propagate the original error.
- TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
- return false;
- }
-
- return true;
-}
-
const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
WriterMutexLock mu(Thread::Current(), dex_lock_);
if (kIsDebugBuild) {
@@ -782,504 +708,81 @@ const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_loc
return nullptr;
}
+std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
+ const char* dex_location, const char* oat_location,
+ std::vector<std::string>* error_msgs) {
+ CHECK(error_msgs != nullptr);
-// Loads all multi dex files from the given oat file returning true on success.
-//
-// Parameters:
-// oat_file - the oat file to load from
-// dex_location - the dex location used to generate the oat file
-// dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
-// generated - whether or not the oat_file existed before or was just (re)generated
-// error_msgs - any error messages will be appended here
-// dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
-static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
- const char* dex_location,
- const uint32_t* dex_location_checksum,
- bool generated,
- std::vector<std::string>* error_msgs,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- if (oat_file == nullptr) {
- return false;
- }
-
- size_t old_size = dex_files->size(); // To rollback on error.
-
- bool success = true;
- for (size_t i = 0; success; ++i) {
- std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
- const char* next_name = next_name_str.c_str();
-
- uint32_t next_location_checksum;
- uint32_t* next_location_checksum_pointer = &next_location_checksum;
- std::string error_msg;
- if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
- // When i=0 the multidex name should be the same as the location name. We already have the
- // checksum it so we don't need to recompute it.
- if (dex_location_checksum == nullptr) {
- next_location_checksum_pointer = nullptr;
- } else {
- next_location_checksum = *dex_location_checksum;
- }
- } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
- DCHECK_EQ(false, i == 0 && generated);
- next_location_checksum_pointer = nullptr;
- }
+ // Verify we aren't holding the mutator lock, which could starve GC if we
+ // have to generate or relocate an oat file.
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current());
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
+ OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
+ !Runtime::Current()->IsAotCompiler());
- if (oat_dex_file == nullptr) {
- if (i == 0 && generated) {
- error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
- " file'%s'", dex_location, next_location_checksum,
- oat_file->GetLocation().c_str());
- error_msgs->push_back(error_msg);
- }
- break; // Not found, done.
- }
-
- // Checksum test. Test must succeed when generated.
- success = !generated;
- if (next_location_checksum_pointer != nullptr) {
- success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
- }
-
- if (success) {
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file.get() == nullptr) {
- success = false;
- error_msgs->push_back(error_msg);
- } else {
- dex_files->push_back(std::move(dex_file));
- }
- }
-
- // When we generated the file, we expect success, or something is terribly wrong.
- CHECK_EQ(false, generated && !success)
- << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
- << std::hex << " dex_location_checksum=" << next_location_checksum
- << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
- }
-
- if (dex_files->size() == old_size) {
- success = false; // We did not even find classes.dex
- }
-
- if (success) {
- return true;
- } else {
- dex_files->erase(dex_files->begin() + old_size, dex_files->end());
- return false;
+ // Lock the target oat location to avoid races generating and loading the
+ // oat file.
+ std::string error_msg;
+ if (!oat_file_assistant.Lock(&error_msg)) {
+ // Don't worry too much if this fails. If it does fail, it's unlikely we
+ // can generate an oat file anyway.
+ VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
}
-}
-
-// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This
-// complicates the loading process, as we should not use an iterative loading process, because that
-// would register the oat file and dex files that come before the broken one. Instead, check all
-// multidex ahead of time.
-bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
- std::vector<std::string>* error_msgs,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- // 1) Check whether we have an open oat file.
- // This requires a dex checksum, use the "primary" one.
- uint32_t dex_location_checksum;
- uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
- bool have_checksum = true;
- std::string checksum_error_msg;
- if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
- // This happens for pre-opted files since the corresponding dex files are no longer on disk.
- dex_location_checksum_pointer = nullptr;
- have_checksum = false;
- }
-
- bool needs_registering = false;
-
- const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
- dex_location_checksum_pointer);
- std::unique_ptr<const OatFile> open_oat_file(
- oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);
-
- // 2) If we do not have an open one, maybe there's one on disk already.
-
- // In case the oat file is not open, we play a locking game here so
- // that if two different processes race to load and register or generate
- // (or worse, one tries to open a partial generated file) we will be okay.
- // This is actually common with apps that use DexClassLoader to work
- // around the dex method reference limit and that have a background
- // service running in a separate process.
- ScopedFlock scoped_flock;
-
- if (open_oat_file.get() == nullptr) {
- if (oat_location != nullptr) {
- // Can only do this if we have a checksum, else error.
- if (!have_checksum) {
- error_msgs->push_back(checksum_error_msg);
- return false;
- }
- std::string error_msg;
-
- // We are loading or creating one in the future. Time to set up the file lock.
- if (!scoped_flock.Init(oat_location, &error_msg)) {
- error_msgs->push_back(error_msg);
- return false;
- }
-
- // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
- open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
- oat_location, &error_msg));
-
- if (open_oat_file.get() == nullptr) {
- std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
- dex_location, oat_location, error_msg.c_str());
- VLOG(class_linker) << compound_msg;
- error_msgs->push_back(compound_msg);
- }
- } else {
- // TODO: What to lock here?
- bool obsolete_file_cleanup_failed;
- open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location,
- dex_location_checksum_pointer,
- kRuntimeISA, error_msgs,
- &obsolete_file_cleanup_failed));
- // There's no point in going forward and eventually try to regenerate the
- // file if we couldn't remove the obsolete one. Mostly likely we will fail
- // with the same error when trying to write the new file.
- // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
- if (obsolete_file_cleanup_failed) {
- return false;
+ // Check if we already have an up-to-date oat file open.
+ const OatFile* source_oat_file = nullptr;
+ {
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
+ for (const OatFile* oat_file : oat_files_) {
+ CHECK(oat_file != nullptr);
+ if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
+ source_oat_file = oat_file;
+ break;
}
}
- needs_registering = true;
}
- // 3) If we have an oat file, check all contained multidex files for our dex_location.
- // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
- bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
- dex_location_checksum_pointer,
- false, error_msgs, dex_files);
- if (success) {
- const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it.
- if (needs_registering) {
- // We opened the oat file, so we must register it.
- RegisterOatFile(oat_file);
+ // If we didn't have an up-to-date oat file open, try to load one from disk.
+ if (source_oat_file == nullptr) {
+ // Update the oat file on disk if we can. This may fail, but that's okay.
+ // Best effort is all that matters here.
+ if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
+ LOG(WARNING) << error_msg;
}
- // If the file isn't executable we failed patchoat but did manage to get the dex files.
- return oat_file->IsExecutable();
- } else {
- if (needs_registering) {
- // We opened it, delete it.
- open_oat_file.reset();
- } else {
- open_oat_file.release(); // Do not delete open oat files.
- }
- }
-
- // 4) If it's not the case (either no oat file or mismatches), regenerate and load.
- // Need a checksum, fail else.
- if (!have_checksum) {
- error_msgs->push_back(checksum_error_msg);
- return false;
+ // Get the oat file on disk.
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ if (oat_file.get() != nullptr) {
+ source_oat_file = oat_file.release();
+ RegisterOatFile(source_oat_file);
+ }
}
- // Look in cache location if no oat_location is given.
- std::string cache_location;
- if (oat_location == nullptr) {
- // Use the dalvik cache.
- const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
- cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str());
- oat_location = cache_location.c_str();
- }
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
- bool has_flock = true;
- // Definitely need to lock now.
- if (!scoped_flock.HasFile()) {
- std::string error_msg;
- if (!scoped_flock.Init(oat_location, &error_msg)) {
- error_msgs->push_back(error_msg);
- has_flock = false;
+ // Load the dex files from the oat file.
+ if (source_oat_file != nullptr) {
+ dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
+ if (dex_files.empty()) {
+ error_msgs->push_back("Failed to open dex files from "
+ + source_oat_file->GetLocation());
}
}
- if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
- // Create the oat file.
- open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
- oat_location, error_msgs));
- }
-
- // Failed, bail.
- if (open_oat_file.get() == nullptr) {
- // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
+ // Fall back to running out of the original dex file if we couldn't load any
+ // dex_files from the oat file.
+ if (dex_files.empty()) {
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- std::string error_msg;
- if (!DexFile::Open(dex_location, dex_location, &error_msg, dex_files)) {
- error_msgs->push_back(error_msg);
+ if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
+ LOG(WARNING) << error_msg;
+ error_msgs->push_back("Failed to open dex files from "
+ + std::string(dex_location));
}
} else {
error_msgs->push_back("Fallback mode disabled, skipping dex files.");
}
- return false;
- }
-
- // Try to load again, but stronger checks.
- success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
- dex_location_checksum_pointer,
- true, error_msgs, dex_files);
- if (success) {
- RegisterOatFile(open_oat_file.release());
- return true;
- } else {
- return false;
- }
-}
-
-const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location,
- uint32_t dex_location_checksum,
- const char* oat_location,
- std::string* error_msg) {
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsAotCompiler(), error_msg));
- if (oat_file.get() == nullptr) {
- *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
- error_msg->c_str());
- return nullptr;
- }
- Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- if (image_space != nullptr) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- uint32_t expected_image_oat_checksum = image_header.GetOatChecksum();
- uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum();
- if (expected_image_oat_checksum != actual_image_oat_checksum) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of "
- "0x%x, found 0x%x", oat_location, expected_image_oat_checksum,
- actual_image_oat_checksum);
- return nullptr;
- }
-
- uintptr_t expected_image_oat_offset = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
- uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin();
- if (expected_image_oat_offset != actual_image_oat_offset) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %"
- PRIuPTR ", found %ud", oat_location, expected_image_oat_offset,
- actual_image_oat_offset);
- return nullptr;
- }
- int32_t expected_patch_delta = image_header.GetPatchDelta();
- int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
- if (expected_patch_delta != actual_patch_delta) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
- " found %d", oat_location, expected_patch_delta, actual_patch_delta);
- return nullptr;
- }
- }
-
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
- &dex_location_checksum);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location,
- dex_location);
- return nullptr;
- }
- uint32_t expected_dex_checksum = dex_location_checksum;
- uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum();
- if (expected_dex_checksum != actual_dex_checksum) {
- *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, "
- "found 0x%x", oat_location, expected_dex_checksum,
- actual_dex_checksum);
- return nullptr;
- }
- std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg));
- if (dex_file.get() != nullptr) {
- return oat_file.release();
- } else {
- return nullptr;
- }
-}
-
-const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location,
- int fd, const char* oat_location,
- std::vector<std::string>* error_msgs) {
- // Generate the output oat file for the dex file
- VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
- std::string error_msg;
- if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) {
- CHECK(!error_msg.empty());
- error_msgs->push_back(error_msg);
- return nullptr;
- }
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsAotCompiler(),
- &error_msg));
- if (oat_file.get() == nullptr) {
- std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
- oat_location, error_msg.c_str());
- error_msgs->push_back(compound_msg);
- return nullptr;
- }
-
- return oat_file.release();
-}
-
-bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file,
- const InstructionSet instruction_set) {
- Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- if (image_space == nullptr) {
- return false;
- }
- uint32_t image_oat_checksum = 0;
- if (instruction_set == kRuntimeISA) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- image_oat_checksum = image_header.GetOatChecksum();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), instruction_set));
- image_oat_checksum = image_header->GetOatChecksum();
- }
- return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum;
-}
-
-bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file,
- const InstructionSet instruction_set,
- std::string* error_msg) {
- Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- if (image_space == nullptr) {
- *error_msg = "No image space for verification against";
- return false;
- }
-
- // If the requested instruction set is the same as the current runtime,
- // we can use the checksums directly. If it isn't, we'll have to read the
- // image header from the image for the right instruction set.
- uint32_t image_oat_checksum = 0;
- uintptr_t image_oat_data_begin = 0;
- int32_t image_patch_delta = 0;
- if (instruction_set == runtime->GetInstructionSet()) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- image_oat_checksum = image_header.GetOatChecksum();
- image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
- image_patch_delta = image_header.GetPatchDelta();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), instruction_set));
- image_oat_checksum = image_header->GetOatChecksum();
- image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
- image_patch_delta = image_header->GetPatchDelta();
- }
- const OatHeader& oat_header = oat_file->GetOatHeader();
- bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum);
-
- // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks.
- if (!oat_file->IsPic()) {
- ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta)
- && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin);
- }
- if (!ret) {
- *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
- oat_file->GetLocation().c_str(),
- oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
- oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
- oat_file->GetOatHeader().GetImagePatchDelta(),
- image_oat_checksum, image_oat_data_begin, image_patch_delta);
- }
- return ret;
-}
-
-bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file,
- const char* dex_location,
- uint32_t dex_location_checksum,
- const InstructionSet instruction_set,
- std::string* error_msg) {
- if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) {
- return false;
- }
-
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
- &dex_location_checksum);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
- oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
- for (const OatFile::OatDexFile* oat_dex_file_in : oat_file->GetOatDexFiles()) {
- *error_msg += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x",
- oat_file->GetLocation().c_str(),
- oat_dex_file_in->GetDexFileLocation().c_str(),
- oat_dex_file_in->GetDexFileLocationChecksum());
- }
- return false;
- }
-
- DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
- return true;
-}
-
-bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
- const char* dex_location,
- const uint32_t* dex_location_checksum,
- std::string* error_msg) {
- CHECK(oat_file != nullptr);
- CHECK(dex_location != nullptr);
- std::unique_ptr<const DexFile> dex_file;
- if (dex_location_checksum == nullptr) {
- // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
- // up-to-date. This is the common case in user builds for jar's and apk's in the /system
- // directory.
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, nullptr);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
- "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
- error_msg->c_str());
- return false;
- }
- dex_file = oat_dex_file->OpenDexFile(error_msg);
- } else {
- bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
- kRuntimeISA, error_msg);
- if (!verified) {
- return false;
- }
- dex_file = oat_file->GetOatDexFile(dex_location,
- dex_location_checksum)->OpenDexFile(error_msg);
- }
- return dex_file.get() != nullptr;
-}
-
-const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
- const char* dex_location,
- const uint32_t* dex_location_checksum,
- InstructionSet isa,
- std::vector<std::string>* error_msgs,
- bool* obsolete_file_cleanup_failed) {
- *obsolete_file_cleanup_failed = false;
- bool already_opened = false;
- std::string dex_location_str(dex_location);
- std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
- &already_opened,
- obsolete_file_cleanup_failed,
- error_msgs));
- std::string error_msg;
- if (oat_file.get() == nullptr) {
- error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
- dex_location));
- return nullptr;
- } else if (oat_file->IsExecutable() &&
- !VerifyOatWithDexFile(oat_file.get(), dex_location,
- dex_location_checksum, &error_msg)) {
- error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
- "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
- error_msg.c_str()));
- return nullptr;
- } else if (!oat_file->IsExecutable() &&
- Runtime::Current()->GetHeap()->HasImageSpace() &&
- !VerifyOatImageChecksum(oat_file.get(), isa)) {
- error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for "
- "dex location '%s'. Image checksum incorrect.",
- oat_file->GetLocation().c_str(), dex_location));
- return nullptr;
- } else {
- return oat_file.release();
}
+ return dex_files;
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
@@ -1294,335 +797,6 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string&
return nullptr;
}
-const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
- InstructionSet isa,
- bool *already_opened,
- bool *obsolete_file_cleanup_failed,
- std::vector<std::string>* error_msgs) {
- // Find out if we've already opened the file
- const OatFile* ret = nullptr;
- std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
- ret = FindOpenedOatFileFromOatLocation(odex_filename);
- if (ret != nullptr) {
- *already_opened = true;
- return ret;
- }
-
- std::string dalvik_cache;
- bool have_android_data = false;
- bool have_dalvik_cache = false;
- bool is_global_cache = false;
- GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
- &have_android_data, &have_dalvik_cache, &is_global_cache);
- std::string cache_filename;
- if (have_dalvik_cache) {
- cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
- ret = FindOpenedOatFileFromOatLocation(cache_filename);
- if (ret != nullptr) {
- *already_opened = true;
- return ret;
- }
- } else {
- // If we need to relocate we should just place odex back where it started.
- cache_filename = odex_filename;
- }
-
- ret = nullptr;
-
- // We know that neither the odex nor the cache'd version is already in use, if it even exists.
- //
- // Now we do the following:
- // 1) Try and open the odex version
- // 2) If present, checksum-verified & relocated correctly return it
- // 3) Close the odex version to free up its address space.
- // 4) Try and open the cache version
- // 5) If present, checksum-verified & relocated correctly return it
- // 6) Close the cache version to free up its address space.
- // 7) If we should relocate:
- // a) If we have opened and checksum-verified the odex version relocate it to
- // 'cache_filename' and return it
- // b) If we have opened and checksum-verified the cache version relocate it in place and return
- // it. This should not happen often (I think only the run-test's will hit this case).
- // 8) If the cache-version was present we should delete it since it must be obsolete if we get to
- // this point.
- // 9) Return nullptr
-
- *already_opened = false;
- const Runtime* runtime = Runtime::Current();
- CHECK(runtime != nullptr);
- bool executable = !runtime->IsAotCompiler();
-
- std::string odex_error_msg;
- bool should_patch_system = false;
- bool odex_checksum_verified = false;
- bool have_system_odex = false;
- {
- // There is a high probability that both these oat files map similar/the same address
- // spaces so we must scope them like this so they each gets its turn.
- std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
- nullptr,
- executable, &odex_error_msg));
- if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
- &odex_checksum_verified,
- &odex_error_msg)) {
- return odex_oat_file.release();
- } else {
- if (odex_checksum_verified) {
- // We can just relocate
- should_patch_system = true;
- odex_error_msg = "Image Patches are incorrect";
- }
- if (odex_oat_file.get() != nullptr) {
- have_system_odex = true;
- }
- }
- }
-
- std::string cache_error_msg;
- bool should_patch_cache = false;
- bool cache_checksum_verified = false;
- if (have_dalvik_cache) {
- std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
- nullptr,
- executable, &cache_error_msg));
- if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
- &cache_checksum_verified,
- &cache_error_msg)) {
- return cache_oat_file.release();
- } else if (cache_checksum_verified) {
- // We can just relocate
- should_patch_cache = true;
- cache_error_msg = "Image Patches are incorrect";
- }
- } else if (have_android_data) {
- // dalvik_cache does not exist but android data does. This means we should be able to create
- // it, so we should try.
- GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
- }
-
- ret = nullptr;
- std::string error_msg;
- if (runtime->CanRelocate()) {
- // Run relocation
- gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace();
- if (space != nullptr) {
- const std::string& image_location = space->GetImageLocation();
- if (odex_checksum_verified && should_patch_system) {
- ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
- } else if (cache_checksum_verified && should_patch_cache) {
- CHECK(have_dalvik_cache);
- ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
- }
- } else if (have_system_odex) {
- ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg);
- }
- }
- if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
- // implicitly: were able to fine where the cached version is but we were unable to use it,
- // either as a destination for relocation or to open a file. We should delete it if it is
- // there.
- if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
- std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
- "searching for dex file %s: %s",
- cache_filename.c_str(), dex_location.c_str(),
- strerror(errno));
- error_msgs->push_back(rm_error_msg);
- VLOG(class_linker) << rm_error_msg;
- // Let the caller know that we couldn't remove the obsolete file.
- // This is a good indication that further writes may fail as well.
- *obsolete_file_cleanup_failed = true;
- }
- }
- if (ret == nullptr) {
- VLOG(class_linker) << error_msg;
- error_msgs->push_back(error_msg);
- std::string relocation_msg;
- if (runtime->CanRelocate()) {
- relocation_msg = StringPrintf(" and relocation failed");
- }
- if (have_dalvik_cache && cache_checksum_verified) {
- error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
- "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
- cache_filename.c_str(), cache_error_msg.c_str(),
- relocation_msg.c_str());
- } else {
- error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
- "dalvik_cache availible)%s.", odex_filename.c_str(),
- odex_error_msg.c_str(), relocation_msg.c_str());
- }
- VLOG(class_linker) << error_msg;
- error_msgs->push_back(error_msg);
- }
- return ret;
-}
-
-const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path,
- InstructionSet isa,
- std::string* error_msg) {
- // We open it non-executable
- std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
- if (output.get() == nullptr) {
- return nullptr;
- }
- if (!Runtime::Current()->GetHeap()->HasImageSpace() ||
- VerifyOatImageChecksum(output.get(), isa)) {
- return output.release();
- } else {
- *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
- oat_path.c_str());
- return nullptr;
- }
-}
-
-const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
- const std::string& output_oat,
- const std::string& image_location,
- InstructionSet isa,
- std::string* error_msg) {
- Runtime* runtime = Runtime::Current();
- DCHECK(runtime != nullptr);
- if (!runtime->GetHeap()->HasImageSpace()) {
- // We don't have an image space so there is no point in trying to patchoat.
- LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
- << "running without an image. Attempting to use oat file for interpretation.";
- return GetInterpretedOnlyOat(input_oat, isa, error_msg);
- }
- if (!runtime->IsDex2OatEnabled()) {
- // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
- // input_oat but make sure we only do interpretation on it's dex files.
- LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
- << "disabled. Attempting to use oat file for interpretation";
- return GetInterpretedOnlyOat(input_oat, isa, error_msg);
- }
- Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
- std::string patchoat(runtime->GetPatchoatExecutable());
-
- std::string isa_arg("--instruction-set=");
- isa_arg += GetInstructionSetString(isa);
- std::string input_oat_filename_arg("--input-oat-file=");
- input_oat_filename_arg += input_oat;
- std::string output_oat_filename_arg("--output-oat-file=");
- output_oat_filename_arg += output_oat;
- std::string patched_image_arg("--patched-image-location=");
- patched_image_arg += image_location;
-
- std::vector<std::string> argv;
- argv.push_back(patchoat);
- argv.push_back(isa_arg);
- argv.push_back(input_oat_filename_arg);
- argv.push_back(output_oat_filename_arg);
- argv.push_back(patched_image_arg);
-
- std::string command_line(Join(argv, ' '));
- LOG(INFO) << "Relocate Oat File: " << command_line;
- bool success = Exec(argv, error_msg);
- if (success) {
- std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
- !runtime->IsAotCompiler(), error_msg));
- bool checksum_verified = false;
- if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
- error_msg)) {
- return output.release();
- } else if (output.get() != nullptr) {
- *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
- "but output file '%s' failed verifcation: %s",
- input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
- } else {
- *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
- "but was unable to open output file '%s': %s",
- input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
- }
- } else if (!runtime->IsAotCompiler()) {
- // patchoat failed which means we probably don't have enough room to place the output oat file,
- // instead of failing we should just run the interpreter from the dex files in the input oat.
- LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
- << "for interpretation. patchoat failure was: " << *error_msg;
- return GetInterpretedOnlyOat(input_oat, isa, error_msg);
- } else {
- *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
- "failed: %s", input_oat.c_str(), output_oat.c_str(),
- error_msg->c_str());
- }
- return nullptr;
-}
-
-bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
- bool* checksum_verified,
- std::string* error_msg) {
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- if (image_space == nullptr) {
- *error_msg = "No image space present";
- return false;
- }
- uint32_t real_image_checksum;
- void* real_image_oat_offset;
- int32_t real_patch_delta;
- if (isa == runtime->GetInstructionSet()) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- real_image_checksum = image_header.GetOatChecksum();
- real_image_oat_offset = image_header.GetOatDataBegin();
- real_patch_delta = image_header.GetPatchDelta();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), isa));
- real_image_checksum = image_header->GetOatChecksum();
- real_image_oat_offset = image_header->GetOatDataBegin();
- real_patch_delta = image_header->GetPatchDelta();
- }
-
- const OatHeader& oat_header = oat_file->GetOatHeader();
- std::string compound_msg;
-
- uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
- *checksum_verified = oat_image_checksum == real_image_checksum;
- if (!*checksum_verified) {
- StringAppendF(&compound_msg, " Oat Image Checksum Incorrect (expected 0x%x, received 0x%x)",
- real_image_checksum, oat_image_checksum);
- }
-
- bool offset_verified;
- bool patch_delta_verified;
-
- if (!oat_file->IsPic()) {
- // If an oat file is not PIC, we need to check that the image is at the expected location and
- // patched in the same way.
- void* oat_image_oat_offset =
- reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
- offset_verified = oat_image_oat_offset == real_image_oat_offset;
- if (!offset_verified) {
- StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
- real_image_oat_offset, oat_image_oat_offset);
- }
-
- int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
- patch_delta_verified = oat_patch_delta == real_patch_delta;
- if (!patch_delta_verified) {
- StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, "
- "received 0x%x)", real_patch_delta, oat_patch_delta);
- }
- } else {
- // If an oat file is PIC, we ignore offset and patching delta.
- offset_verified = true;
- patch_delta_verified = true;
- }
-
- bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
- if (!ret) {
- *error_msg = "Oat file failed to verify:" + compound_msg;
- }
- return ret;
-}
-
-const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
- std::string* error_msg) {
- const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
- if (oat_file != nullptr) {
- return oat_file;
- }
- return OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsAotCompiler(), error_msg);
-}
-
void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
DCHECK(obj != nullptr);
@@ -2259,8 +1433,8 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
return nullptr;
} else if (result.get() == nullptr) {
// broken loader - throw NPE to be compatible with Dalvik
- ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
- class_name_string.c_str()).c_str());
+ ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
+ class_name_string.c_str()).c_str());
return nullptr;
} else {
// success, return mirror::Class*