diff options
author | 2021-03-18 22:23:04 +0000 | |
---|---|---|
committer | 2021-03-25 18:08:51 +0000 | |
commit | a129d8aef0dfe39edf39b5537852bc2c60496bfa (patch) | |
tree | b5eded46436a3ff1b371a6a706cb7cf8a7e0878b | |
parent | ec7b19485ba4af8b0fd0804389c470b556322d13 (diff) |
Revamp vdex format for better extensibility.
- Remove class loader context and boot classpath checksum sections.
Those are not needed now that VerifierDeps don't depend on them.
- Remove remaining quickening encoding.
- Introduce sections in a vdex file, which can be extended without
requiring a version change.
Test: test.py
Bug: 160294863
Change-Id: I4e3e25f34d242dc4de37f30ba9d78bcffbc1436d
-rw-r--r-- | dex2oat/dex2oat.cc | 16 | ||||
-rw-r--r-- | dex2oat/dex2oat_vdex_test.cc | 2 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer.cc | 84 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer_test.cc | 3 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 25 | ||||
-rw-r--r-- | openjdkjvmti/fixed_up_dex_file.cc | 24 | ||||
-rw-r--r-- | runtime/oat_file.cc | 4 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 2 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 10 | ||||
-rw-r--r-- | runtime/runtime.cc | 21 | ||||
-rw-r--r-- | runtime/vdex_file.cc | 313 | ||||
-rw-r--r-- | runtime/vdex_file.h | 293 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/src/Main.java | 8 |
13 files changed, 201 insertions, 604 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 1c63a86971..c8a2cbdcb7 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1287,8 +1287,8 @@ class Dex2Oat final { // the information to remain valid. if (update_input_vdex_) { File* vdex_file = vdex_files_.back().get(); - if (!vdex_file->PwriteFully(&VdexFile::VerifierDepsHeader::kVdexInvalidMagic, - arraysize(VdexFile::VerifierDepsHeader::kVdexInvalidMagic), + if (!vdex_file->PwriteFully(&VdexFile::VdexFileHeader::kVdexInvalidMagic, + arraysize(VdexFile::VdexFileHeader::kVdexInvalidMagic), /*offset=*/ 0u)) { PLOG(ERROR) << "Failed to invalidate vdex header. File: " << vdex_file->GetPath(); return false; @@ -1850,18 +1850,6 @@ class Dex2Oat final { // Setup vdex for compilation. const std::vector<const DexFile*>& dex_files = compiler_options_->dex_files_for_oat_file_; - if (!DoEagerUnquickeningOfVdex() && input_vdex_file_ != nullptr) { - // TODO: we unquicken unconditionally, as we don't know - // if the boot image has changed. How exactly we'll know is under - // experimentation. - TimingLogger::ScopedTiming time_unquicken("Unquicken", timings_); - - // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening - // optimization does not depend on the boot image (the optimization relies on not - // having final fields in a class, which does not change for an app). - input_vdex_file_->Unquicken(dex_files, /* decompile_return_instruction */ false); - } - // To allow initialization of classes that construct ThreadLocal objects in class initializer, // re-initialize the ThreadLocal.nextHashCode to a new object that's not in the boot image. ThreadLocalHashOverride thread_local_hash_override( diff --git a/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc index fd511083e1..d9a555a3ea 100644 --- a/dex2oat/dex2oat_vdex_test.cc +++ b/dex2oat/dex2oat_vdex_test.cc @@ -81,7 +81,7 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { } // Verify the deps. - VdexFile::VerifierDepsHeader vdex_header = vdex->GetVerifierDepsHeader(); + VdexFile::VdexFileHeader vdex_header = vdex->GetVdexFileHeader(); if (!vdex_header.IsValid()) { ::testing::AssertionFailure() << "Invalid vdex header"; } diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 9070ed2d4f..3e2136089b 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -606,8 +606,9 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, DCHECK(write_state_ == WriteState::kAddingDexFileSources); DCHECK(vdex_file.HasDexSection()); const uint8_t* current_dex_data = nullptr; - for (size_t i = 0; i < vdex_file.GetVerifierDepsHeader().GetNumberOfDexFiles(); ++i) { - current_dex_data = vdex_file.GetNextDexFileData(current_dex_data); + size_t i = 0; + for (; i < vdex_file.GetNumberOfDexFiles(); ++i) { + current_dex_data = vdex_file.GetNextDexFileData(current_dex_data, i); if (current_dex_data == nullptr) { LOG(ERROR) << "Unexpected number of dex files in vdex " << location; return false; @@ -629,7 +630,7 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, header->file_size_); } - if (vdex_file.GetNextDexFileData(current_dex_data) != nullptr) { + if (vdex_file.GetNextDexFileData(current_dex_data, i) != nullptr) { LOG(ERROR) << "Unexpected number of dex files in vdex " << location; return false; } @@ -693,9 +694,10 @@ bool OatWriter::WriteAndOpenDexFiles( /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { CHECK(write_state_ == WriteState::kAddingDexFileSources); - // Reserve space for Vdex header and checksums. - vdex_size_ = sizeof(VdexFile::VerifierDepsHeader) + - oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); + size_vdex_header_ = sizeof(VdexFile::VdexFileHeader) + + VdexSection::kNumberOfSections * sizeof(VdexFile::VdexSectionHeader); + // Reserve space for Vdex header, sections, and checksums. + vdex_size_ = size_vdex_header_ + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); // Write DEX files into VDEX, mmap and open them. std::vector<MemMap> dex_files_map; @@ -3133,8 +3135,6 @@ bool OatWriter::WriteDexFiles(File* file, } if (extract_dex_files_into_vdex_) { - // Add the dex section header. - vdex_size_ += sizeof(VdexFile::DexSectionHeader); vdex_dex_files_offset_ = vdex_size_; // Perform dexlayout if requested. @@ -3155,8 +3155,6 @@ bool OatWriter::WriteDexFiles(File* file, for (OatDexFile& oat_dex_file : oat_dex_files_) { // Dex files are required to be 4 byte aligned. vdex_size_with_dex_files = RoundUp(vdex_size_with_dex_files, 4u); - // Leave extra room for the quicken table offset. - vdex_size_with_dex_files += sizeof(VdexFile::QuickeningTableOffsetType); // Record offset for the dex file. oat_dex_file.dex_file_offset_ = vdex_size_with_dex_files; // Add the size of the dex file. @@ -3236,18 +3234,9 @@ bool OatWriter::WriteDexFiles(File* file, // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { // Dex files are required to be 4 byte aligned. - size_t quickening_table_offset_offset = RoundUp(vdex_size_, 4u); - if (!update_input_vdex) { - // Clear the padding. - memset(vdex_begin_ + vdex_size_, 0, quickening_table_offset_offset - vdex_size_); - // Initialize the quickening table offset to 0. - auto* quickening_table_offset = reinterpret_cast<VdexFile::QuickeningTableOffsetType*>( - vdex_begin_ + quickening_table_offset_offset); - *quickening_table_offset = 0u; - } - size_dex_file_alignment_ += quickening_table_offset_offset - vdex_size_; - size_quickening_table_offset_ += sizeof(VdexFile::QuickeningTableOffsetType); - vdex_size_ = quickening_table_offset_offset + sizeof(VdexFile::QuickeningTableOffsetType); + size_t old_vdex_size = vdex_size_; + vdex_size_ = RoundUp(vdex_size_, 4u); + size_dex_file_alignment_ += vdex_size_ - old_vdex_size; // Write the actual dex file. if (!WriteDexFile(file, &oat_dex_file, update_input_vdex)) { return false; @@ -3729,8 +3718,6 @@ bool OatWriter::FinishVdexFile(File* vdex_file, verifier::VerifierDeps* verifier buffer.reserve(64 * KB); WriteVerifierDeps(verifier_deps, &buffer); DCHECK_EQ(vdex_size_, old_vdex_size + buffer.size()); - WriteQuickeningInfo(&buffer); - DCHECK_EQ(vdex_size_, old_vdex_size + buffer.size()); // Resize the vdex file. if (vdex_file->SetLength(vdex_size_) != 0) { @@ -3788,7 +3775,7 @@ bool OatWriter::FinishVdexFile(File* vdex_file, verifier::VerifierDeps* verifier } // Write checksums - off_t checksums_offset = sizeof(VdexFile::VerifierDepsHeader); + off_t checksums_offset = VdexFile::GetChecksumsOffset(); VdexFile::VdexChecksum* checksums_data = reinterpret_cast<VdexFile::VdexChecksum*>(vdex_begin + checksums_offset); for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) { @@ -3797,24 +3784,29 @@ bool OatWriter::FinishVdexFile(File* vdex_file, verifier::VerifierDeps* verifier size_vdex_checksums_ += sizeof(VdexFile::VdexChecksum); } - // Maybe write dex section header. - DCHECK_NE(vdex_verifier_deps_offset_, 0u); - DCHECK_NE(vdex_quickening_info_offset_, 0u); + // Write sections. + uint8_t* ptr = vdex_begin + sizeof(VdexFile::VdexFileHeader); - bool has_dex_section = extract_dex_files_into_vdex_; - if (has_dex_section) { - DCHECK_NE(vdex_dex_files_offset_, 0u); - size_t dex_section_size = vdex_dex_shared_data_offset_ - vdex_dex_files_offset_; - size_t dex_shared_data_size = vdex_verifier_deps_offset_ - vdex_dex_shared_data_offset_; - size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_; + // Checksums section. + new (ptr) VdexFile::VdexSectionHeader(VdexSection::kChecksumSection, + checksums_offset, + size_vdex_checksums_); + ptr += sizeof(VdexFile::VdexFileHeader); - void* dex_section_header_storage = checksums_data + oat_dex_files_.size(); - new (dex_section_header_storage) VdexFile::DexSectionHeader(dex_section_size, - dex_shared_data_size, - quickening_info_section_size); - size_vdex_header_ += sizeof(VdexFile::DexSectionHeader); - } + // Dex section. + new (ptr) VdexFile::VdexSectionHeader( + VdexSection::kDexFileSection, + extract_dex_files_into_vdex_ ? vdex_dex_files_offset_ : 0u, + extract_dex_files_into_vdex_ ? vdex_verifier_deps_offset_ - vdex_dex_files_offset_ : 0u); + ptr += sizeof(VdexFile::VdexFileHeader); + + // VerifierDeps section. + new (ptr) VdexFile::VdexSectionHeader(VdexSection::kVerifierDepsSection, + vdex_verifier_deps_offset_, + vdex_size_ - vdex_verifier_deps_offset_); + // All the contents (except the header) of the vdex file has been emitted in memory. Flush it + // to disk. { TimingLogger::ScopedTiming split("VDEX flush contents", timings_); // Sync the data to the disk while the header is invalid. We do not want to end up with @@ -3833,14 +3825,10 @@ bool OatWriter::FinishVdexFile(File* vdex_file, verifier::VerifierDeps* verifier } } - // Write header. - // TODO: Use `size_quickening_info_` instead of `verifier_deps_section_size` which - // includes `size_quickening_info_alignment_`, adjust code in VdexFile. - size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_; - - new (vdex_begin) VdexFile::VerifierDepsHeader( - oat_dex_files_.size(), verifier_deps_section_size, has_dex_section); - size_vdex_header_ += sizeof(VdexFile::VerifierDepsHeader); + // Now that we know all contents have been flushed to disk, we can write + // the header which will mke the vdex usable. + bool has_dex_section = extract_dex_files_into_vdex_; + new (vdex_begin) VdexFile::VdexFileHeader(has_dex_section); // Note: If `extract_dex_files_into_vdex_`, we passed the ownership of the vdex dex file // MemMap to the caller, so we need to use msync() for the range explicitly. diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index e77e361570..6cb3ac8dd9 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -291,9 +291,6 @@ class OatTest : public CommonCompilerDriverTest { dex_file_data->GetHeader().file_size_)); ASSERT_EQ(dex_file_data->GetLocation(), opened_dex_file->GetLocation()); } - const VdexFile::DexSectionHeader &vdex_header = - opened_oat_file->GetVdexFile()->GetDexSectionHeader(); - ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 4b381d0016..f389d7858e 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -569,7 +569,7 @@ class OatDumper { if (!options_.dump_header_only_) { VariableIndentationOutputStream vios(&os); - VdexFile::VerifierDepsHeader vdex_header = oat_file_.GetVdexFile()->GetVerifierDepsHeader(); + VdexFile::VdexFileHeader vdex_header = oat_file_.GetVdexFile()->GetVdexFileHeader(); if (vdex_header.IsValid()) { std::string error_msg; std::vector<const DexFile*> dex_files; @@ -590,10 +590,8 @@ class OatDumper { } else { os << "UNRECOGNIZED vdex file, magic " << vdex_header.GetMagic() - << ", verifier deps version " - << vdex_header.GetVerifierDepsVersion() - << ", dex section version " - << vdex_header.GetDexSectionVersion() + << ", version " + << vdex_header.GetVdexVersion() << "\n"; } for (size_t i = 0; i < oat_dex_files_.size(); i++) { @@ -614,9 +612,9 @@ class OatDumper { } DexFileUniqV vdex_dex_files; - std::unique_ptr<const VdexFile> vdex_file = OpenVdexUnquicken(vdex_filename, - &vdex_dex_files, - &error_msg); + std::unique_ptr<const VdexFile> vdex_file = OpenVdex(vdex_filename, + &vdex_dex_files, + &error_msg); if (vdex_file.get() == nullptr) { os << "Failed to open vdex file: " << error_msg << "\n"; return false; @@ -764,10 +762,10 @@ class OatDumper { } // Returns nullptr and updates error_msg if the Vdex file cannot be opened, otherwise all Dex - // files are fully unquickened and stored in dex_files - std::unique_ptr<const VdexFile> OpenVdexUnquicken(const std::string& vdex_filename, - /* out */ DexFileUniqV* dex_files, - /* out */ std::string* error_msg) { + // files are stored in dex_files. + std::unique_ptr<const VdexFile> OpenVdex(const std::string& vdex_filename, + /* out */ DexFileUniqV* dex_files, + /* out */ std::string* error_msg) { std::unique_ptr<const File> file(OS::OpenFileForReading(vdex_filename.c_str())); if (file == nullptr) { *error_msg = "Could not open file " + vdex_filename + " for reading."; @@ -806,9 +804,6 @@ class OatDumper { return nullptr; } - vdex_file->Unquicken(MakeNonOwningPointerVector(tmp_dex_files), - /* decompile_return_instruction= */ true); - *dex_files = std::move(tmp_dex_files); return vdex_file; } diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index eefbe41503..4aa81ff47e 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -51,28 +51,6 @@ static void RecomputeDexChecksum(art::DexFile* dex_file) { dex_file->CalculateChecksum(); } -static const art::VdexFile* GetVdex(const art::DexFile& original_dex_file) { - const art::OatDexFile* oat_dex = original_dex_file.GetOatDexFile(); - if (oat_dex == nullptr) { - return nullptr; - } - const art::OatFile* oat_file = oat_dex->GetOatFile(); - if (oat_file == nullptr) { - return nullptr; - } - return oat_file->GetVdexFile(); -} - -static void DoDexUnquicken(const art::DexFile& new_dex_file, - const art::DexFile& original_dex_file) { - const art::VdexFile* vdex = GetVdex(original_dex_file); - if (vdex != nullptr) { - vdex->UnquickenDexFile(new_dex_file, - original_dex_file, - /* decompile_return_instruction= */ true); - } -} - static void DCheckVerifyDexFile(const art::DexFile& dex) { if (art::kIsDebugBuild) { std::string error; @@ -150,8 +128,6 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi new_dex_file->SetHiddenapiDomain(original.GetHiddenapiDomain()); - DoDexUnquicken(*new_dex_file, original); - RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get())); DCheckVerifyDexFile(*new_dex_file); std::unique_ptr<FixedUpDexFile> ret(new FixedUpDexFile(std::move(new_dex_file), std::move(data))); diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index dc311c8b4f..115e0eadfc 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1549,9 +1549,9 @@ class OatFileBackedByVdex final : public OatFileBase { std::unique_ptr<OatFileBackedByVdex> oat_file(new OatFileBackedByVdex(vdex_file->GetName())); if (vdex_file->HasDexSection()) { uint32_t i = 0; - for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr); + for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr, i); dex_file_start != nullptr; - dex_file_start = vdex_file->GetNextDexFileData(dex_file_start), ++i) { + dex_file_start = vdex_file->GetNextDexFileData(dex_file_start, ++i)) { // Create the OatDexFile and add it to the owning container. std::string location = DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()); std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(location.c_str()); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 0fee0efa5b..245ae36622 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -337,7 +337,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* er return true; } - uint32_t number_of_dex_files = file.GetVerifierDepsHeader().GetNumberOfDexFiles(); + uint32_t number_of_dex_files = file.GetNumberOfDexFiles(); if (required_dex_checksums->size() != number_of_dex_files) { *error_msg = StringPrintf("expected %zu dex files but found %u", required_dex_checksums->size(), diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 25a24134ba..80afd91312 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -594,15 +594,6 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat_ DCHECK(context->OpenDexFiles()) << "Context created from already opened dex files should not attempt to open again"; - // Check that we can use the vdex against this boot class path and in this class loader context. - // Note 1: We do not need a class loader collision check because there is no compiled code. - // Note 2: If these checks fail, we cannot fast-verify because the vdex does not contain - // full VerifierDeps. - if (!vdex_file->MatchesBootClassPathChecksums() || - !vdex_file->MatchesClassLoaderContext(*context.get())) { - return dex_files; - } - // Initialize an OatFile instance backed by the loaded vdex. std::unique_ptr<OatFile> oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files), std::move(vdex_file), @@ -761,7 +752,6 @@ class BackgroundVerificationTask final : public Task { if (!VdexFile::WriteToDisk(vdex_path_, dex_files_, verifier_deps, - class_loader_context_, &error_msg)) { LOG(ERROR) << "Could not write anonymous vdex " << vdex_path_ << ": " << error_msg; return; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index a4e98f2e2a..6e7167d6e9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -2958,27 +2958,6 @@ void Runtime::DeoptimizeBootImage() { jit->GetCodeCache()->TransitionToDebuggable(); } } - // Also de-quicken all -quick opcodes. We do this for both BCP and non-bcp so if we are swapping - // debuggable during startup by a plugin (eg JVMTI) even non-BCP code has its vdex files deopted. - std::unordered_set<const VdexFile*> vdexs; - GetClassLinker()->VisitKnownDexFiles(Thread::Current(), [&](const art::DexFile* df) { - const OatDexFile* odf = df->GetOatDexFile(); - if (odf == nullptr) { - return; - } - const OatFile* of = odf->GetOatFile(); - if (of == nullptr || of->IsDebuggable()) { - // no Oat or already debuggable so no -quick. - return; - } - vdexs.insert(of->GetVdexFile()); - }); - LOG(INFO) << "Unquickening " << vdexs.size() << " vdex files!"; - for (const VdexFile* vf : vdexs) { - vf->AllowWriting(true); - vf->UnquickenInPlace(/*decompile_return_instruction=*/true); - vf->AllowWriting(false); - } } Runtime::ScopedThreadPoolUsage::ScopedThreadPoolUsage() diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 1f2d4e51bc..02a15634e3 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -45,56 +45,24 @@ namespace art { -constexpr uint8_t VdexFile::VerifierDepsHeader::kVdexInvalidMagic[4]; -constexpr uint8_t VdexFile::VerifierDepsHeader::kVdexMagic[4]; -constexpr uint8_t VdexFile::VerifierDepsHeader::kVerifierDepsVersion[4]; -constexpr uint8_t VdexFile::VerifierDepsHeader::kDexSectionVersion[4]; -constexpr uint8_t VdexFile::VerifierDepsHeader::kDexSectionVersionEmpty[4]; +constexpr uint8_t VdexFile::VdexFileHeader::kVdexInvalidMagic[4]; +constexpr uint8_t VdexFile::VdexFileHeader::kVdexMagic[4]; +constexpr uint8_t VdexFile::VdexFileHeader::kVdexVersion[4]; -bool VdexFile::VerifierDepsHeader::IsMagicValid() const { +bool VdexFile::VdexFileHeader::IsMagicValid() const { return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0); } -bool VdexFile::VerifierDepsHeader::IsVerifierDepsVersionValid() const { - return (memcmp(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion)) == 0); +bool VdexFile::VdexFileHeader::IsVdexVersionValid() const { + return (memcmp(vdex_version_, kVdexVersion, sizeof(kVdexVersion)) == 0); } -bool VdexFile::VerifierDepsHeader::IsDexSectionVersionValid() const { - return (memcmp(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)) == 0) || - (memcmp(dex_section_version_, kDexSectionVersionEmpty, sizeof(kDexSectionVersionEmpty)) == 0); -} - -bool VdexFile::VerifierDepsHeader::HasDexSection() const { - return (memcmp(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)) == 0); -} - -VdexFile::VerifierDepsHeader::VerifierDepsHeader(uint32_t number_of_dex_files, - uint32_t verifier_deps_size, - bool has_dex_section, - uint32_t bootclasspath_checksums_size, - uint32_t class_loader_context_size) - : number_of_dex_files_(number_of_dex_files), - verifier_deps_size_(verifier_deps_size), - bootclasspath_checksums_size_(bootclasspath_checksums_size), - class_loader_context_size_(class_loader_context_size) { +VdexFile::VdexFileHeader::VdexFileHeader(bool has_dex_section ATTRIBUTE_UNUSED) + : number_of_sections_(static_cast<uint32_t>(VdexSection::kNumberOfSections)) { memcpy(magic_, kVdexMagic, sizeof(kVdexMagic)); - memcpy(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion)); - if (has_dex_section) { - memcpy(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)); - } else { - memcpy(dex_section_version_, kDexSectionVersionEmpty, sizeof(kDexSectionVersionEmpty)); - } + memcpy(vdex_version_, kVdexVersion, sizeof(kVdexVersion)); DCHECK(IsMagicValid()); - DCHECK(IsVerifierDepsVersionValid()); - DCHECK(IsDexSectionVersionValid()); -} - -VdexFile::DexSectionHeader::DexSectionHeader(uint32_t dex_size, - uint32_t dex_shared_data_size, - uint32_t quickening_info_size) - : dex_size_(dex_size), - dex_shared_data_size_(dex_shared_data_size), - quickening_info_size_(quickening_info_size) { + DCHECK(IsVdexVersionValid()); } std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, @@ -182,66 +150,32 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, return nullptr; } - if (unquicken && vdex->HasDexSection()) { - std::vector<std::unique_ptr<const DexFile>> unique_ptr_dex_files; - if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) { - return nullptr; - } - // TODO: It would be nice to avoid doing the return-instruction stuff but then we end up not - // being able to tell if we need dequickening later. Instead just get rid of that too. - vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), - /* decompile_return_instruction= */ true); - // Update the quickening info size to pretend there isn't any. - size_t offset = vdex->GetDexSectionHeaderOffset(); - reinterpret_cast<DexSectionHeader*>(vdex->mmap_.Begin() + offset)->quickening_info_size_ = 0; - } - - if (!writable) { - vdex->AllowWriting(false); - Runtime* runtime = Runtime::Current(); - // The runtime might not be available at this point if we're running - // dex2oat or oatdump. - if (runtime != nullptr) { - size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeVdex(); - Runtime::MadviseFileForRange(madvise_size_limit, - vdex->Size(), - vdex->Begin(), - vdex->End(), - vdex_filename); - } - } - - return vdex; } -const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { +const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor, uint32_t dex_file_index) const { DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); if (cursor == nullptr) { // Beginning of the iteration, return the first dex file if there is one. - return HasDexSection() ? DexBegin() + sizeof(QuickeningTableOffsetType) : nullptr; + return HasDexSection() ? DexBegin() : nullptr; + } else if (dex_file_index >= GetNumberOfDexFiles()) { + return nullptr; } else { // Fetch the next dex file. Return null if there is none. const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_; // Dex files are required to be 4 byte aligned. the OatWriter makes sure they are, see // OatWriter::SeekToDexFiles. - data = AlignUp(data, 4); - - return (data == DexEnd()) ? nullptr : data + sizeof(QuickeningTableOffsetType); + return AlignUp(data, 4); } } -void VdexFile::AllowWriting(bool val) const { - CHECK(mmap_.Protect(val ? (PROT_READ | PROT_WRITE) : PROT_READ)); -} - bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files, std::string* error_msg) const { const ArtDexFileLoader dex_file_loader; size_t i = 0; - for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr); + for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr, i); dex_file_start != nullptr; - dex_file_start = GetNextDexFileData(dex_file_start), ++i) { + dex_file_start = GetNextDexFileData(dex_file_start, ++i)) { size_t size = reinterpret_cast<const DexFile::Header*>(dex_file_start)->file_size_; // TODO: Supply the location information for a vdex file. static constexpr char kVdexLocation[] = ""; @@ -265,128 +199,6 @@ bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_ return true; } -void VdexFile::UnquickenInPlace(bool decompile_return_instruction) const { - CHECK_NE(mmap_.GetProtect() & PROT_WRITE, 0) - << "File not mapped writable. Cannot unquicken! " << mmap_; - if (HasDexSection()) { - std::vector<std::unique_ptr<const DexFile>> unique_ptr_dex_files; - std::string error_msg; - if (!OpenAllDexFiles(&unique_ptr_dex_files, &error_msg)) { - return; - } - Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), - decompile_return_instruction); - // Update the quickening info size to pretend there isn't any. - size_t offset = GetDexSectionHeaderOffset(); - reinterpret_cast<DexSectionHeader*>(mmap_.Begin() + offset)->quickening_info_size_ = 0; - } -} - -void VdexFile::Unquicken(const std::vector<const DexFile*>& target_dex_files, - bool decompile_return_instruction) const { - const uint8_t* source_dex = GetNextDexFileData(nullptr); - for (const DexFile* target_dex : target_dex_files) { - UnquickenDexFile(*target_dex, source_dex, decompile_return_instruction); - source_dex = GetNextDexFileData(source_dex); - } - DCHECK(source_dex == nullptr); -} - -uint32_t VdexFile::GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const { - DCHECK_GE(source_dex_begin, DexBegin()); - DCHECK_LT(source_dex_begin, DexEnd()); - return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1]; -} - -CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable( - const uint8_t* source_dex_begin, - const ArrayRef<const uint8_t>& quickening_info) const { - // The offset a is in preheader right before the dex file. - const uint32_t offset = GetQuickeningInfoTableOffset(source_dex_begin); - return CompactOffsetTable::Accessor(quickening_info.SubArray(offset).data()); -} - -CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable( - const DexFile& dex_file, - const ArrayRef<const uint8_t>& quickening_info) const { - return GetQuickenInfoOffsetTable(dex_file.Begin(), quickening_info); -} - -static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info, - uint32_t quickening_offset) { - // Subtract offset of one since 0 represents unused and cannot be in the table. - ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset - 1); - return remaining.SubArray(0u, QuickenInfoTable::SizeInBytes(remaining)); -} - -void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, - const DexFile& source_dex_file, - bool decompile_return_instruction) const { - UnquickenDexFile( - target_dex_file, source_dex_file.Begin(), decompile_return_instruction); -} - -void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, - const uint8_t* source_dex_begin, - bool decompile_return_instruction) const { - ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); - if (quickening_info.empty()) { - // Bail early if there is no quickening info and no need to decompile. This means there is also - // no RETURN_VOID to decompile since the empty table takes a non zero amount of space. - return; - } - // Make sure to not unquicken the same code item multiple times. - std::unordered_set<const dex::CodeItem*> unquickened_code_item; - CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, - quickening_info)); - for (ClassAccessor class_accessor : target_dex_file.GetClasses()) { - for (const ClassAccessor::Method& method : class_accessor.GetMethods()) { - const dex::CodeItem* code_item = method.GetCodeItem(); - if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - const uint32_t offset = accessor.GetOffset(method.GetIndex()); - // Offset being 0 means not quickened. - if (offset != 0u) { - ArrayRef<const uint8_t> quicken_data = GetQuickeningInfoAt(quickening_info, offset); - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); - } - } - } - } -} - -ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, - uint32_t dex_method_idx) const { - ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); - if (quickening_info.empty()) { - return ArrayRef<const uint8_t>(); - } - CHECK_LT(dex_method_idx, dex_file.NumMethodIds()); - const uint32_t quickening_offset = - GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx); - if (quickening_offset == 0u) { - return ArrayRef<const uint8_t>(); - } - return GetQuickeningInfoAt(quickening_info, quickening_offset); -} - -static std::string ComputeBootClassPathChecksumString() { - Runtime* const runtime = Runtime::Current(); - // Do not include boot image extension checksums, use their dex file checksums instead. Unlike - // oat files, vdex files do not reference anything in image spaces, so there is no reason why - // loading or not loading a boot image extension would affect the validity of the vdex file. - // Note: Update of a boot class path module such as conscrypt invalidates the vdex file anyway. - ArrayRef<gc::space::ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces()); - size_t boot_image_components = - image_spaces.empty() ? 0u : image_spaces[0]->GetImageHeader().GetComponentCount(); - return gc::space::ImageSpace::GetBootClassPathChecksums( - image_spaces.SubArray(/*pos=*/ 0u, boot_image_components), - ArrayRef<const DexFile* const>(runtime->GetClassLinker()->GetBootClassPath())); -} - static bool CreateDirectories(const std::string& child_path, /* out */ std::string* error_msg) { size_t last_slash_pos = child_path.find_last_of('/'); CHECK_NE(last_slash_pos, std::string::npos) << "Invalid path: " << child_path; @@ -407,19 +219,29 @@ static bool CreateDirectories(const std::string& child_path, /* out */ std::stri bool VdexFile::WriteToDisk(const std::string& path, const std::vector<const DexFile*>& dex_files, const verifier::VerifierDeps& verifier_deps, - const std::string& class_loader_context, std::string* error_msg) { std::vector<uint8_t> verifier_deps_data; verifier_deps.Encode(dex_files, &verifier_deps_data); - std::string boot_checksum = ComputeBootClassPathChecksumString(); - DCHECK_NE(boot_checksum, ""); + VdexFile::VdexFileHeader vdex_header(/* has_dex_section= */ false); + VdexFile::VdexSectionHeader sections[static_cast<uint32_t>(VdexSection::kNumberOfSections)]; + + // Set checksum section. + sections[VdexSection::kChecksumSection].section_kind = VdexSection::kChecksumSection; + sections[VdexSection::kChecksumSection].section_offset = GetChecksumsOffset(); + sections[VdexSection::kChecksumSection].section_size = + sizeof(VdexFile::VdexChecksum) * dex_files.size(); - VdexFile::VerifierDepsHeader deps_header(dex_files.size(), - verifier_deps_data.size(), - /* has_dex_section= */ false, - boot_checksum.size(), - class_loader_context.size()); + // Set dex section. + sections[VdexSection::kDexFileSection].section_kind = VdexSection::kDexFileSection; + sections[VdexSection::kDexFileSection].section_offset = 0u; + sections[VdexSection::kDexFileSection].section_size = 0u; + + // Set VerifierDeps section. + sections[VdexSection::kVerifierDepsSection].section_kind = VdexSection::kVerifierDepsSection; + sections[VdexSection::kVerifierDepsSection].section_offset = + GetChecksumsOffset() + sections[kChecksumSection].section_size; + sections[VdexSection::kVerifierDepsSection].section_size = verifier_deps_data.size(); if (!CreateDirectories(path, error_msg)) { return false; @@ -431,12 +253,21 @@ bool VdexFile::WriteToDisk(const std::string& path, return false; } - if (!out->WriteFully(reinterpret_cast<const char*>(&deps_header), sizeof(deps_header))) { + // Write header. + if (!out->WriteFully(reinterpret_cast<const char*>(&vdex_header), sizeof(vdex_header))) { *error_msg = "Could not write vdex header to " + path; out->Unlink(); return false; } + // Write section infos. + if (!out->WriteFully(reinterpret_cast<const char*>(§ions), sizeof(sections))) { + *error_msg = "Could not write vdex sections to " + path; + out->Unlink(); + return false; + } + + // Write checksum section. for (const DexFile* dex_file : dex_files) { const uint32_t* checksum_ptr = &dex_file->GetHeader().checksum_; static_assert(sizeof(*checksum_ptr) == sizeof(VdexFile::VdexChecksum)); @@ -444,7 +275,7 @@ bool VdexFile::WriteToDisk(const std::string& path, sizeof(VdexFile::VdexChecksum))) { *error_msg = "Could not write dex checksums to " + path; out->Unlink(); - return false; + return false; } } @@ -455,18 +286,6 @@ bool VdexFile::WriteToDisk(const std::string& path, return false; } - if (!out->WriteFully(boot_checksum.c_str(), boot_checksum.size())) { - *error_msg = "Could not write boot classpath checksum to " + path; - out->Unlink(); - return false; - } - - if (!out->WriteFully(class_loader_context.c_str(), class_loader_context.size())) { - *error_msg = "Could not write class loader context to " + path; - out->Unlink(); - return false; - } - if (out->FlushClose() != 0) { *error_msg = "Could not flush and close " + path; out->Unlink(); @@ -478,13 +297,12 @@ bool VdexFile::WriteToDisk(const std::string& path, bool VdexFile::MatchesDexFileChecksums(const std::vector<const DexFile::Header*>& dex_headers) const { - const VerifierDepsHeader& header = GetVerifierDepsHeader(); - if (dex_headers.size() != header.GetNumberOfDexFiles()) { + if (dex_headers.size() != GetNumberOfDexFiles()) { LOG(WARNING) << "Mismatch of number of dex files in vdex (expected=" - << header.GetNumberOfDexFiles() << ", actual=" << dex_headers.size() << ")"; + << GetNumberOfDexFiles() << ", actual=" << dex_headers.size() << ")"; return false; } - const VdexChecksum* checksums = header.GetDexChecksumsArray(); + const VdexChecksum* checksums = GetDexChecksumsArray(); for (size_t i = 0; i < dex_headers.size(); ++i) { if (checksums[i] != dex_headers[i]->checksum_) { LOG(WARNING) << "Mismatch of dex file checksum in vdex (index=" << i << ")"; @@ -494,32 +312,6 @@ bool VdexFile::MatchesDexFileChecksums(const std::vector<const DexFile::Header*> return true; } -bool VdexFile::MatchesBootClassPathChecksums() const { - ArrayRef<const uint8_t> data = GetBootClassPathChecksumData(); - std::string vdex(reinterpret_cast<const char*>(data.data()), data.size()); - std::string runtime = ComputeBootClassPathChecksumString(); - if (vdex == runtime) { - return true; - } else { - LOG(WARNING) << "Mismatch of boot class path checksum in vdex (expected=" - << vdex << ", actual=" << runtime << ")"; - return false; - } -} - -bool VdexFile::MatchesClassLoaderContext(const ClassLoaderContext& context) const { - ArrayRef<const uint8_t> data = GetClassLoaderContextData(); - std::string spec(reinterpret_cast<const char*>(data.data()), data.size()); - ClassLoaderContext::VerificationResult result = context.VerifyClassLoaderContextMatch(spec); - if (result != ClassLoaderContext::VerificationResult::kMismatch) { - return true; - } else { - LOG(WARNING) << "Mismatch of class loader context in vdex (expected=" - << spec << ", actual=" << context.EncodeContextForOatFile("") << ")"; - return false; - } -} - static ObjPtr<mirror::Class> FindClassAndClearException(ClassLinker* class_linker, Thread* self, const char* name, @@ -577,14 +369,15 @@ ClassStatus VdexFile::ComputeClassStatus(Thread* self, Handle<mirror::Class> cls // Find which dex file index from within the vdex file. uint32_t index = 0; - for (; index < GetVerifierDepsHeader().GetNumberOfDexFiles(); ++index) { + for (; index < GetNumberOfDexFiles(); ++index) { if (dex_file.GetLocationChecksum() == GetLocationChecksum(index)) { break; } } - DCHECK_NE(index, GetVerifierDepsHeader().GetNumberOfDexFiles()); - const uint8_t* verifier_deps = GetVerifierDepsStart(); + DCHECK_NE(index, GetNumberOfDexFiles()); + + const uint8_t* verifier_deps = GetVerifierDepsData().data(); const uint32_t* dex_file_class_defs = GetDexFileClassDefs(verifier_deps, index); // Fetch type checks offsets. diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 6e11728061..4dabfd4910 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -49,18 +49,18 @@ class VerifierDeps; // In the description below, D is the number of dex files. // // File format: -// VdexFile::VerifierDepsHeader fixed-length header -// Dex file checksums +// VdexFileHeader fixed-length header +// VdexSectionHeader[kNumberOfSections] // -// Optionally: -// VdexFile::DexSectionHeader fixed-length header +// Checksum section +// VdexChecksum[D] // -// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. -// DEX[0] array of the input DEX files, the bytecode may have been quickened. -// quicken_table_off[1] -// DEX[1] -// ... -// DEX[D-1] +// Optionally: +// DexSection +// DEX[0] array of the input DEX files +// DEX[1] +// ... +// DEX[D-1] // // VerifierDeps // 4-byte alignment @@ -75,58 +75,45 @@ class VerifierDeps; // uint32 Number of strings // uint32[] String data offsets for each string // uint8[] String data -// -// -// Optionally: -// QuickeningInfo -// uint8[D][] quickening data -// uint32[D][] quickening data offset tables + + +enum VdexSection : uint32_t { + kChecksumSection = 0, + kDexFileSection = 1, + kVerifierDepsSection = 2, + kNumberOfSections = 3, +}; class VdexFile { public: using VdexChecksum = uint32_t; - using QuickeningTableOffsetType = uint32_t; - struct VerifierDepsHeader { + struct VdexSectionHeader { + VdexSection section_kind; + uint32_t section_offset; + uint32_t section_size; + + VdexSectionHeader(VdexSection kind, uint32_t offset, uint32_t size) + : section_kind(kind), section_offset(offset), section_size(size) {} + + VdexSectionHeader() {} + }; + + struct VdexFileHeader { public: - VerifierDepsHeader(uint32_t number_of_dex_files_, - uint32_t verifier_deps_size, - bool has_dex_section, - uint32_t bootclasspath_checksums_size = 0, - uint32_t class_loader_context_size = 0); + explicit VdexFileHeader(bool has_dex_section); const char* GetMagic() const { return reinterpret_cast<const char*>(magic_); } - const char* GetVerifierDepsVersion() const { - return reinterpret_cast<const char*>(verifier_deps_version_); + const char* GetVdexVersion() const { + return reinterpret_cast<const char*>(vdex_version_); } - const char* GetDexSectionVersion() const { - return reinterpret_cast<const char*>(dex_section_version_); + uint32_t GetNumberOfSections() const { + return number_of_sections_; } bool IsMagicValid() const; - bool IsVerifierDepsVersionValid() const; - bool IsDexSectionVersionValid() const; + bool IsVdexVersionValid() const; bool IsValid() const { - return IsMagicValid() && IsVerifierDepsVersionValid() && IsDexSectionVersionValid(); - } - bool HasDexSection() const; - - uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; } - uint32_t GetNumberOfDexFiles() const { return number_of_dex_files_; } - uint32_t GetBootClassPathChecksumStringSize() const { return bootclasspath_checksums_size_; } - uint32_t GetClassLoaderContextStringSize() const { return class_loader_context_size_; } - - size_t GetSizeOfChecksumsSection() const { - return sizeof(VdexChecksum) * GetNumberOfDexFiles(); - } - - const VdexChecksum* GetDexChecksumsArray() const { - return reinterpret_cast<const VdexChecksum*>( - reinterpret_cast<const uint8_t*>(this) + sizeof(VerifierDepsHeader)); - } - - VdexChecksum GetDexChecksumAtOffset(size_t idx) const { - DCHECK_LT(idx, GetNumberOfDexFiles()); - return GetDexChecksumsArray()[idx]; + return IsMagicValid() && IsVdexVersionValid(); } static constexpr uint8_t kVdexInvalidMagic[] = { 'w', 'd', 'e', 'x' }; @@ -135,65 +122,62 @@ class VdexFile { static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; // The format version of the verifier deps header and the verifier deps. - // Last update: Fast per-class access. - static constexpr uint8_t kVerifierDepsVersion[] = { '0', '2', '6', '\0' }; - - // The format version of the dex section header and the dex section, containing - // both the dex code and the quickening data. - // Last update: Add owned section for CompactDex. - static constexpr uint8_t kDexSectionVersion[] = { '0', '0', '2', '\0' }; - - // If the .vdex file has no dex section (hence no dex code nor quickening data), - // we encode this magic version. - static constexpr uint8_t kDexSectionVersionEmpty[] = { '0', '0', '0', '\0' }; + // Last update: Introduce vdex sections. + static constexpr uint8_t kVdexVersion[] = { '0', '2', '7', '\0' }; uint8_t magic_[4]; - uint8_t verifier_deps_version_[4]; - uint8_t dex_section_version_[4]; - uint32_t number_of_dex_files_; - uint32_t verifier_deps_size_; - uint32_t bootclasspath_checksums_size_; - uint32_t class_loader_context_size_; + uint8_t vdex_version_[4]; + uint32_t number_of_sections_; }; - struct DexSectionHeader { - public: - DexSectionHeader(uint32_t dex_size, - uint32_t dex_shared_data_size, - uint32_t quickening_info_size); - - uint32_t GetDexSize() const { return dex_size_; } - uint32_t GetDexSharedDataSize() const { return dex_shared_data_size_; } - uint32_t GetQuickeningInfoSize() const { return quickening_info_size_; } - - size_t GetDexSectionSize() const { - return sizeof(DexSectionHeader) + - GetDexSize() + - GetDexSharedDataSize(); - } + const VdexSectionHeader& GetSectionHeaderAt(uint32_t index) const { + DCHECK_LT(index, GetVdexFileHeader().GetNumberOfSections()); + return *reinterpret_cast<const VdexSectionHeader*>( + Begin() + sizeof(VdexFileHeader) + index * sizeof(VdexSectionHeader)); + } - private: - uint32_t dex_size_; - uint32_t dex_shared_data_size_; - uint32_t quickening_info_size_; + const VdexSectionHeader& GetSectionHeader(VdexSection kind) const { + return GetSectionHeaderAt(static_cast<uint32_t>(kind)); + } - friend class VdexFile; // For updating quickening_info_size_. - }; + static size_t GetChecksumsOffset() { + return sizeof(VdexFileHeader) + + static_cast<size_t>(VdexSection::kNumberOfSections) * sizeof(VdexSectionHeader); + } size_t GetComputedFileSize() const { - size_t size = sizeof(VerifierDepsHeader); - const VerifierDepsHeader& header = GetVerifierDepsHeader(); - size += header.GetVerifierDepsSize(); - size += header.GetSizeOfChecksumsSection(); - if (header.HasDexSection()) { - size += GetDexSectionHeader().GetDexSectionSize(); - size += GetDexSectionHeader().GetQuickeningInfoSize(); + const VdexFileHeader& header = GetVdexFileHeader(); + uint32_t size = sizeof(VdexFileHeader) + + header.GetNumberOfSections() * sizeof(VdexSectionHeader); + for (uint32_t i = 0; i < header.GetNumberOfSections(); ++i) { + size = std::max(size, + GetSectionHeaderAt(i).section_offset + GetSectionHeaderAt(i).section_size); } - size += header.GetBootClassPathChecksumStringSize(); - size += header.GetClassLoaderContextStringSize(); return size; } + bool IsDexSectionValid() const; + + bool HasDexSection() const { + return GetSectionHeader(VdexSection::kDexFileSection).section_size != 0u; + } + uint32_t GetVerifierDepsSize() const { + return GetSectionHeader(VdexSection::kVerifierDepsSection).section_size; + } + uint32_t GetNumberOfDexFiles() const { + return GetSectionHeader(VdexSection::kChecksumSection).section_size / sizeof(VdexChecksum); + } + + const VdexChecksum* GetDexChecksumsArray() const { + return reinterpret_cast<const VdexChecksum*>( + Begin() + GetSectionHeader(VdexSection::kChecksumSection).section_offset); + } + + VdexChecksum GetDexChecksumAt(size_t idx) const { + DCHECK_LT(idx, GetNumberOfDexFiles()); + return GetDexChecksumsArray()[idx]; + } + // Note: The file is called "primary" to match the naming with profiles. static const constexpr char* kVdexNameInDmFile = "primary.vdex"; @@ -263,98 +247,36 @@ class VdexFile { const uint8_t* End() const { return mmap_.End(); } size_t Size() const { return mmap_.Size(); } - const VerifierDepsHeader& GetVerifierDepsHeader() const { - return *reinterpret_cast<const VerifierDepsHeader*>(Begin()); - } - - uint32_t GetDexSectionHeaderOffset() const { - return sizeof(VerifierDepsHeader) + GetVerifierDepsHeader().GetSizeOfChecksumsSection(); - } - - const DexSectionHeader& GetDexSectionHeader() const { - DCHECK(GetVerifierDepsHeader().HasDexSection()); - return *reinterpret_cast<const DexSectionHeader*>(Begin() + GetDexSectionHeaderOffset()); - } - - const uint8_t* GetVerifierDepsStart() const { - const uint8_t* result = Begin() + GetDexSectionHeaderOffset(); - if (GetVerifierDepsHeader().HasDexSection()) { - // When there is a dex section, the verifier deps are after it, but before the quickening. - return result + GetDexSectionHeader().GetDexSectionSize(); - } else { - // When there is no dex section, the verifier deps are just after the header. - return result; - } + const VdexFileHeader& GetVdexFileHeader() const { + return *reinterpret_cast<const VdexFileHeader*>(Begin()); } ArrayRef<const uint8_t> GetVerifierDepsData() const { return ArrayRef<const uint8_t>( - GetVerifierDepsStart(), - GetVerifierDepsHeader().GetVerifierDepsSize()); - } - - ArrayRef<const uint8_t> GetQuickeningInfo() const { - return ArrayRef<const uint8_t>( - GetVerifierDepsData().end(), - GetVerifierDepsHeader().HasDexSection() - ? GetDexSectionHeader().GetQuickeningInfoSize() : 0); - } - - ArrayRef<const uint8_t> GetBootClassPathChecksumData() const { - return ArrayRef<const uint8_t>( - GetQuickeningInfo().end(), - GetVerifierDepsHeader().GetBootClassPathChecksumStringSize()); - } - - ArrayRef<const uint8_t> GetClassLoaderContextData() const { - return ArrayRef<const uint8_t>( - GetBootClassPathChecksumData().end(), - GetVerifierDepsHeader().GetClassLoaderContextStringSize()); + Begin() + GetSectionHeader(VdexSection::kVerifierDepsSection).section_offset, + GetSectionHeader(VdexSection::kVerifierDepsSection).section_size); } bool IsValid() const { - return mmap_.Size() >= sizeof(VerifierDepsHeader) && GetVerifierDepsHeader().IsValid(); + return mmap_.Size() >= sizeof(VdexFileHeader) && GetVdexFileHeader().IsValid(); } // This method is for iterating over the dex files in the vdex. If `cursor` is null, // the first dex file is returned. If `cursor` is not null, it must point to a dex // file and this method returns the next dex file if there is one, or null if there // is none. - const uint8_t* GetNextDexFileData(const uint8_t* cursor) const; + const uint8_t* GetNextDexFileData(const uint8_t* cursor, uint32_t dex_file_index) const; // Get the location checksum of the dex file number `dex_file_index`. uint32_t GetLocationChecksum(uint32_t dex_file_index) const { - DCHECK_LT(dex_file_index, GetVerifierDepsHeader().GetNumberOfDexFiles()); - return reinterpret_cast<const uint32_t*>(Begin() + sizeof(VerifierDepsHeader))[dex_file_index]; + DCHECK_LT(dex_file_index, GetNumberOfDexFiles()); + return GetDexChecksumAt(dex_file_index); } // Open all the dex files contained in this vdex file. bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files, std::string* error_msg) const; - // In-place unquicken the given `dex_files` based on `quickening_info`. - // `decompile_return_instruction` controls if RETURN_VOID_BARRIER instructions are - // decompiled to RETURN_VOID instructions using the slower ClassAccessor instead of the faster - // QuickeningInfoIterator. - // Always unquickens using the vdex dex files as the source for quicken tables. - void Unquicken(const std::vector<const DexFile*>& target_dex_files, - bool decompile_return_instruction) const; - - void UnquickenInPlace(bool decompile_return_instruction) const; - - // Fully unquicken `target_dex_file` based on `quickening_info`. - void UnquickenDexFile(const DexFile& target_dex_file, - const DexFile& source_dex_file, - bool decompile_return_instruction) const; - - // Return the quickening info of a given method index (or null if it's empty). - ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file, - uint32_t dex_method_idx) const; - - bool HasDexSection() const { - return GetVerifierDepsHeader().HasDexSection(); - } - // Writes a vdex into `path` and returns true on success. // The vdex will not contain a dex section but will store checksums of `dex_files`, // encoded `verifier_deps`, as well as the current boot class path cheksum and @@ -362,7 +284,6 @@ class VdexFile { static bool WriteToDisk(const std::string& path, const std::vector<const DexFile*>& dex_files, const verifier::VerifierDeps& verifier_deps, - const std::string& class_loader_context, std::string* error_msg); // Returns true if the dex file checksums stored in the vdex header match @@ -370,17 +291,6 @@ class VdexFile { // order must match too. bool MatchesDexFileChecksums(const std::vector<const DexFile::Header*>& dex_headers) const; - // Returns true if the boot class path checksum stored in the vdex matches - // the checksum of boot class path in the current runtime. - bool MatchesBootClassPathChecksums() const; - - // Returns true if the class loader context stored in the vdex matches `context`. - bool MatchesClassLoaderContext(const ClassLoaderContext& context) const; - - // Make the Vdex file & underlying dex-files RW or RO. Should only be used for in-place - // dequickening. - void AllowWriting(bool value) const; - ClassStatus ComputeClassStatus(Thread* self, Handle<mirror::Class> cls) const REQUIRES_SHARED(Locks::mutator_lock_); @@ -391,35 +301,14 @@ class VdexFile { } private: - uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const; - - // Source dex must be the in the vdex file. - void UnquickenDexFile(const DexFile& target_dex_file, - const uint8_t* source_dex_begin, - bool decompile_return_instruction) const; - - CompactOffsetTable::Accessor GetQuickenInfoOffsetTable( - const DexFile& dex_file, - const ArrayRef<const uint8_t>& quickening_info) const; - - CompactOffsetTable::Accessor GetQuickenInfoOffsetTable( - const uint8_t* source_dex_begin, - const ArrayRef<const uint8_t>& quickening_info) const; - bool ContainsDexFile(const DexFile& dex_file) const; const uint8_t* DexBegin() const { DCHECK(HasDexSection()); - return Begin() + GetDexSectionHeaderOffset() + sizeof(DexSectionHeader); - } - - const uint8_t* DexEnd() const { - DCHECK(HasDexSection()); - return DexBegin() + GetDexSectionHeader().GetDexSize(); + return Begin() + GetSectionHeader(VdexSection::kDexFileSection).section_offset; } - // mutable for AllowWriting() - mutable MemMap mmap_; + MemMap mmap_; DISALLOW_COPY_AND_ASSIGN(VdexFile); }; diff --git a/test/692-vdex-inmem-loader/src/Main.java b/test/692-vdex-inmem-loader/src/Main.java index 3ebe2c1cda..d7701fbba1 100644 --- a/test/692-vdex-inmem-loader/src/Main.java +++ b/test/692-vdex-inmem-loader/src/Main.java @@ -103,12 +103,14 @@ public class Main { test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, /*invokeMethod*/ true); - // Change boot classpath checksum. + // Change boot classpath checksum. vdex files can still be loaded. appendToBootClassLoader(DEX_EXTRA, /*isCorePlatform*/ false); loaders = multiLoader(); - test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ false); - test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true); + test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, + /*invokeMethod*/ false); + test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, + /*invokeMethod*/ true); loaders = multiLoader(); test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, |