From: David Brazdil Date: Thu, 1 Sep 2016 10:06:18 +0000 (+0100) Subject: Introduce VDEX file, use it for DEX files X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=7b49e6cade09bc65b3b5f22d45fc9d0a7184e4f2;p=android-x86%2Fart.git Introduce VDEX file, use it for DEX files This patch introduces a new output file called VDEX. In the future, VDEX files will store pre-validated DEX files which do not need to be re-extracted and re-verified when recompiling, e.g. due to new profiling information or after a system update. With this CL, the OatWriter writes DEX files into the VDEX and the rest of its output into OAT. The OatFile class and related classes are updated to load the VDEX at runtime and mmap the DEX file section from it. Patchoat creates symlinks to the source VDEX files in the target directory or copies the files if passed in as file descriptors. The feature can be disabled by setting the environment variable ART_ENABLE_VDEX to false. Test: m test-art-host Bug: 30937355 Change-Id: I54dcaececf6814c258c80524ec15e2e2ef69c8dd --- diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 04a034463..845b272aa 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -244,6 +244,11 @@ ifeq ($(ART_BUILD_HOST_STATIC),true) art_cflags += -DART_BUILD_HOST_STATIC=1 endif +# Temporary flag allowing to disable recent changes in oat file management. +ifneq ($(ART_ENABLE_VDEX),false) + art_cflags += -DART_ENABLE_VDEX +endif + # Cflags for non-debug ART and ART tools. art_non_debug_cflags := \ $(ART_NDEBUG_OPT_FLAG) diff --git a/build/art.go b/build/art.go index 9cab3b9fc..da4609d83 100644 --- a/build/art.go +++ b/build/art.go @@ -43,6 +43,10 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { cflags = append(cflags, "-DART_USE_TLAB=1") } + if !envFalse(ctx, "ART_ENABLE_VDEX") { + cflags = append(cflags, "-DART_ENABLE_VDEX") + } + imtSize := envDefault(ctx, "ART_IMT_SIZE", "43") cflags = append(cflags, "-DIMT_SIZE="+imtSize) @@ -228,3 +232,7 @@ func envDefault(ctx android.BaseContext, key string, defaultValue string) string func envTrue(ctx android.BaseContext, key string) bool { return ctx.AConfig().Getenv(key) == "true" } + +func envFalse(ctx android.BaseContext, key string) bool { + return ctx.AConfig().Getenv(key) == "false" +} diff --git a/compiler/image_test.cc b/compiler/image_test.cc index e1ee0d296..a18935f09 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -73,10 +73,12 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { CHECK_EQ(0, mkdir_result) << image_dir; ScratchFile image_file(OS::CreateEmptyFile(image_filename.c_str())); - std::string oat_filename(image_filename, 0, image_filename.size() - 3); - oat_filename += "oat"; + std::string oat_filename = ReplaceFileExtension(image_filename, "oat"); ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str())); + std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex"); + ScratchFile vdex_file(OS::CreateEmptyFile(vdex_filename.c_str())); + const uintptr_t requested_image_base = ART_BASE_ADDRESS; std::unordered_map dex_file_to_oat_index_map; std::vector oat_filename_vector(1, oat_filename.c_str()); @@ -109,7 +111,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { oat_file.GetFile()); elf_writer->Start(); OatWriter oat_writer(/*compiling_boot_image*/true, &timings); - OutputStream* rodata = elf_writer->StartRoData(); + OutputStream* oat_rodata = elf_writer->StartRoData(); for (const DexFile* dex_file : dex_files) { ArrayRef raw_dex_file( reinterpret_cast(&dex_file->GetHeader()), @@ -120,16 +122,18 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { } std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; - bool dex_files_ok = oat_writer.WriteAndOpenDexFiles( - rodata, - oat_file.GetFile(), - compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - &key_value_store, - /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. - &opened_dex_files_map, - &opened_dex_files); - ASSERT_TRUE(dex_files_ok); + { + bool dex_files_ok = oat_writer.WriteAndOpenDexFiles( + kIsVdexEnabled ? vdex_file.GetFile() : oat_file.GetFile(), + oat_rodata, + compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), + &key_value_store, + /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. + &opened_dex_files_map, + &opened_dex_files); + ASSERT_TRUE(dex_files_ok); + } bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); @@ -138,17 +142,17 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { instruction_set_features_.get()); oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files, &patcher); size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer.GetSize() - rodata_size; + size_t text_size = oat_writer.GetOatSize() - rodata_size; elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize()); writer->UpdateOatFileLayout(/* oat_index */ 0u, elf_writer->GetLoadedSize(), oat_writer.GetOatDataOffset(), - oat_writer.GetSize()); + oat_writer.GetOatSize()); - bool rodata_ok = oat_writer.WriteRodata(rodata); + bool rodata_ok = oat_writer.WriteRodata(oat_rodata); ASSERT_TRUE(rodata_ok); - elf_writer->EndRoData(rodata); + elf_writer->EndRoData(oat_rodata); OutputStream* text = elf_writer->StartText(); bool text_ok = oat_writer.WriteCode(text); @@ -285,6 +289,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { image_file.Unlink(); oat_file.Unlink(); + vdex_file.Unlink(); int rmdir_result = rmdir(image_dir.c_str()); CHECK_EQ(0, rmdir_result); } diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index b1e38113e..78e9ca91b 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -125,7 +125,8 @@ class OatTest : public CommonCompilerTest { /* profile_compilation_info */ nullptr)); } - bool WriteElf(File* file, + bool WriteElf(File* vdex_file, + File* oat_file, const std::vector& dex_files, SafeMap& key_value_store, bool verify) { @@ -141,10 +142,11 @@ class OatTest : public CommonCompilerTest { return false; } } - return DoWriteElf(file, oat_writer, key_value_store, verify); + return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify); } - bool WriteElf(File* file, + bool WriteElf(File* vdex_file, + File* oat_file, const std::vector& dex_filenames, SafeMap& key_value_store, bool verify) { @@ -155,10 +157,11 @@ class OatTest : public CommonCompilerTest { return false; } } - return DoWriteElf(file, oat_writer, key_value_store, verify); + return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify); } - bool WriteElf(File* file, + bool WriteElf(File* vdex_file, + File* oat_file, File&& zip_fd, const char* location, SafeMap& key_value_store, @@ -168,10 +171,11 @@ class OatTest : public CommonCompilerTest { if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) { return false; } - return DoWriteElf(file, oat_writer, key_value_store, verify); + return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify); } - bool DoWriteElf(File* file, + bool DoWriteElf(File* vdex_file, + File* oat_file, OatWriter& oat_writer, SafeMap& key_value_store, bool verify) { @@ -179,13 +183,13 @@ class OatTest : public CommonCompilerTest { compiler_driver_->GetInstructionSet(), compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), - file); + oat_file); elf_writer->Start(); - OutputStream* rodata = elf_writer->StartRoData(); + OutputStream* oat_rodata = elf_writer->StartRoData(); std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; - if (!oat_writer.WriteAndOpenDexFiles(rodata, - file, + if (!oat_writer.WriteAndOpenDexFiles(kIsVdexEnabled ? vdex_file : oat_file, + oat_rodata, compiler_driver_->GetInstructionSet(), compiler_driver_->GetInstructionSetFeatures(), &key_value_store, @@ -206,13 +210,13 @@ class OatTest : public CommonCompilerTest { instruction_set_features_.get()); oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files, &patcher); size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer.GetSize() - rodata_size; + size_t text_size = oat_writer.GetOatSize() - rodata_size; elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize()); - if (!oat_writer.WriteRodata(rodata)) { + if (!oat_writer.WriteRodata(oat_rodata)) { return false; } - elf_writer->EndRoData(rodata); + elf_writer->EndRoData(oat_rodata); OutputStream* text = elf_writer->StartText(); if (!oat_writer.WriteCode(text)) { @@ -366,17 +370,21 @@ TEST_F(OatTest, WriteRead) { compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2); } - ScratchFile tmp; + ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex"); SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "lue.art"); - bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store, false); + bool success = WriteElf(tmp_vdex.GetFile(), + tmp_oat.GetFile(), + class_linker->GetBootClassPath(), + key_value_store, + false); ASSERT_TRUE(success); if (kCompile) { // OatWriter strips the code, regenerate to compare compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); } - std::unique_ptr oat_file(OatFile::Open(tmp.GetFilename(), - tmp.GetFilename(), + std::unique_ptr oat_file(OatFile::Open(tmp_oat.GetFilename(), + tmp_oat.GetFilename(), nullptr, nullptr, false, @@ -498,14 +506,14 @@ TEST_F(OatTest, EmptyTextSection) { compiler_driver_->SetDexFilesForOatFile(dex_files); compiler_driver_->CompileAll(class_loader, dex_files, &timings); - ScratchFile tmp; + ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex"); SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store, false); + bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), dex_files, key_value_store, false); ASSERT_TRUE(success); - std::unique_ptr oat_file(OatFile::Open(tmp.GetFilename(), - tmp.GetFilename(), + std::unique_ptr oat_file(OatFile::Open(tmp_oat.GetFilename(), + tmp_oat.GetFilename(), nullptr, nullptr, false, @@ -513,7 +521,8 @@ TEST_F(OatTest, EmptyTextSection) { nullptr, &error_msg)); ASSERT_TRUE(oat_file != nullptr); - EXPECT_LT(static_cast(oat_file->Size()), static_cast(tmp.GetFile()->GetLength())); + EXPECT_LT(static_cast(oat_file->Size()), + static_cast(tmp_oat.GetFile()->GetLength())); } static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr& data) { @@ -559,10 +568,14 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb) { ASSERT_TRUE(success); input_filenames.push_back(dex_file2.GetFilename().c_str()); - ScratchFile oat_file; + ScratchFile oat_file, vdex_file(oat_file, ".vdex"); SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify); + success = WriteElf(vdex_file.GetFile(), + oat_file.GetFile(), + input_filenames, + key_value_store, + verify); // In verify mode, we expect failure. if (verify) { @@ -668,8 +681,9 @@ void OatTest::TestZipFileInput(bool verify) { // Test using the AddDexFileSource() interface with the zip file. std::vector input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] - ScratchFile oat_file; - success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify); + ScratchFile oat_file, vdex_file(oat_file, ".vdex"); + success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), + input_filenames, key_value_store, verify); if (verify) { ASSERT_FALSE(success); @@ -713,8 +727,9 @@ void OatTest::TestZipFileInput(bool verify) { File zip_fd(dup(zip_file.GetFd()), /* check_usage */ false); ASSERT_NE(-1, zip_fd.Fd()); - ScratchFile oat_file; - success = WriteElf(oat_file.GetFile(), + ScratchFile oat_file, vdex_file(oat_file, ".vdex"); + success = WriteElf(vdex_file.GetFile(), + oat_file.GetFile(), std::move(zip_fd), zip_file.GetFilename().c_str(), key_value_store, diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c9c5d2467..43e01d54a 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -39,6 +39,8 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "image_writer.h" +#include "linker/buffered_output_stream.h" +#include "linker/file_output_stream.h" #include "linker/multi_oat_relative_patcher.h" #include "linker/output_stream.h" #include "mirror/array.h" @@ -51,6 +53,7 @@ #include "scoped_thread_state_change.h" #include "type_lookup_table.h" #include "utils/dex_cache_arrays_layout-inl.h" +#include "vdex_file.h" #include "verifier/method_verifier.h" #include "zip_archive.h" @@ -283,10 +286,13 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) image_writer_(nullptr), compiling_boot_image_(compiling_boot_image), dex_files_(nullptr), - size_(0u), + vdex_size_(0u), + vdex_dex_files_offset_(0u), + oat_size_(0u), bss_size_(0u), oat_data_offset_(0u), oat_header_(nullptr), + size_vdex_header_(0), size_dex_file_alignment_(0), size_executable_offset_alignment_(0), size_oat_header_(0), @@ -421,8 +427,8 @@ dchecked_vector OatWriter::GetSourceLocations() const { } bool OatWriter::WriteAndOpenDexFiles( - OutputStream* rodata, - File* file, + File* vdex_file, + OutputStream* oat_rodata, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, SafeMap* key_value_store, @@ -431,37 +437,67 @@ bool OatWriter::WriteAndOpenDexFiles( /*out*/ std::vector>* opened_dex_files) { CHECK(write_state_ == WriteState::kAddingDexFileSources); - size_t offset = InitOatHeader(instruction_set, - instruction_set_features, - dchecked_integral_cast(oat_dex_files_.size()), - key_value_store); - size_ = InitOatDexFiles(offset); + // Record the ELF rodata section offset, i.e. the beginning of the OAT data. + if (!RecordOatDataOffset(oat_rodata)) { + return false; + } std::unique_ptr dex_files_map; std::vector> dex_files; - if (!WriteDexFiles(rodata, file) || - !OpenDexFiles(file, verify, &dex_files_map, &dex_files)) { - return false; - } - // Do a bulk checksum update for Dex[]. Doing it piece by piece would be - // difficult because we're not using the OutputStream directly. - if (!oat_dex_files_.empty()) { - size_t size = size_ - oat_dex_files_[0].dex_file_offset_; - oat_header_->UpdateChecksum(dex_files_map->Begin(), size); + // Initialize VDEX and OAT headers. + if (kIsVdexEnabled) { + size_vdex_header_ = sizeof(VdexFile::Header); + vdex_size_ = size_vdex_header_; } + size_t oat_data_offset = InitOatHeader(instruction_set, + instruction_set_features, + dchecked_integral_cast(oat_dex_files_.size()), + key_value_store); + oat_size_ = InitOatDexFiles(oat_data_offset); + + ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get()); + + if (kIsVdexEnabled) { + std::unique_ptr vdex_out( + MakeUnique(MakeUnique(vdex_file))); + + // Write DEX files into VDEX, mmap and open them. + if (!WriteDexFiles(vdex_out.get(), vdex_file) || + !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { + return false; + } + + // VDEX is finalized. Seek to the beginning of the file and write the header. + if (!WriteVdexHeader(vdex_out.get())) { + return false; + } + } else { + // Write DEX files into OAT, mmap and open them. + if (!WriteDexFiles(oat_rodata, vdex_file) || + !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { + return false; + } - ChecksumUpdatingOutputStream checksum_updating_rodata(rodata, oat_header_.get()); + // Do a bulk checksum update for Dex[]. Doing it piece by piece would be + // difficult because we're not using the OutputStream directly. + if (!oat_dex_files_.empty()) { + size_t size = oat_size_ - oat_dex_files_[0].dex_file_offset_; + oat_header_->UpdateChecksum(dex_files_map->Begin(), size); + } + } + // Write TypeLookupTables into OAT. if (!WriteTypeLookupTables(&checksum_updating_rodata, dex_files)) { return false; } - // Reserve space for class offsets and update class_offsets_offset_. + // Reserve space for class offsets in OAT and update class_offsets_offset_. for (OatDexFile& oat_dex_file : oat_dex_files_) { oat_dex_file.ReserveClassOffsets(this); } + // Write OatDexFiles into OAT. Needs to be done last, once offsets are collected. if (!WriteOatDexFiles(&checksum_updating_rodata)) { return false; } @@ -490,7 +526,7 @@ void OatWriter::PrepareLayout(const CompilerDriver* compiler, InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); CHECK_EQ(instruction_set, oat_header_->GetInstructionSet()); - uint32_t offset = size_; + uint32_t offset = oat_size_; { TimingLogger::ScopedTiming split("InitOatClasses", timings_); offset = InitOatClasses(offset); @@ -507,11 +543,11 @@ void OatWriter::PrepareLayout(const CompilerDriver* compiler, TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_); offset = InitOatCodeDexFiles(offset); } - size_ = offset; + oat_size_ = offset; if (!HasBootImage()) { // Allocate space for app dex cache arrays in the .bss section. - size_t bss_start = RoundUp(size_, kPageSize); + size_t bss_start = RoundUp(oat_size_, kPageSize); PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); bss_size_ = 0u; for (const DexFile* dex_file : *dex_files_) { @@ -1587,6 +1623,7 @@ bool OatWriter::WriteCode(OutputStream* out) { VLOG(compiler) << #x "=" << PrettySize(x) << " (" << (x) << "B)"; \ size_total += (x); + DO_STAT(size_vdex_header_); DO_STAT(size_dex_file_alignment_); DO_STAT(size_executable_offset_alignment_); DO_STAT(size_oat_header_); @@ -1622,13 +1659,14 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_class_method_offsets_); #undef DO_STAT - VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \ - CHECK_EQ(file_offset + size_total, static_cast(oat_end_file_offset)); - CHECK_EQ(size_, size_total); + VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; + + CHECK_EQ(vdex_size_ + oat_size_, size_total); + CHECK_EQ(file_offset + size_total - vdex_size_, static_cast(oat_end_file_offset)); } - CHECK_EQ(file_offset + size_, static_cast(oat_end_file_offset)); - CHECK_EQ(size_, relative_offset); + CHECK_EQ(file_offset + oat_size_, static_cast(oat_end_file_offset)); + CHECK_EQ(oat_size_, relative_offset); write_state_ = WriteState::kWriteHeader; return true; @@ -1831,17 +1869,14 @@ bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* loc return true; } -bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) { - TimingLogger::ScopedTiming split("WriteDexFiles", timings_); +bool OatWriter::WriteDexFiles(OutputStream* out, File* file) { + TimingLogger::ScopedTiming split("Write Dex files", timings_); - // Get the elf file offset of the oat file. - if (!RecordOatDataOffset(rodata)) { - return false; - } + vdex_dex_files_offset_ = vdex_size_; // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!WriteDexFile(rodata, file, &oat_dex_file)) { + if (!WriteDexFile(out, file, &oat_dex_file)) { return false; } } @@ -1856,45 +1891,50 @@ bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) { return true; } -bool OatWriter::WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file) { - if (!SeekToDexFile(rodata, file, oat_dex_file)) { +bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { + if (!SeekToDexFile(out, file, oat_dex_file)) { return false; } if (oat_dex_file->source_.IsZipEntry()) { - if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { + if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { return false; } } else if (oat_dex_file->source_.IsRawFile()) { - if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { + if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { return false; } } else { DCHECK(oat_dex_file->source_.IsRawData()); - if (!WriteDexFile(rodata, oat_dex_file, oat_dex_file->source_.GetRawData())) { + if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData())) { return false; } } // Update current size and account for the written data. - DCHECK_EQ(size_, oat_dex_file->dex_file_offset_); - size_ += oat_dex_file->dex_file_size_; + if (kIsVdexEnabled) { + DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); + vdex_size_ += oat_dex_file->dex_file_size_; + } else { + DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_); + oat_size_ += oat_dex_file->dex_file_size_; + } size_dex_file_ += oat_dex_file->dex_file_size_; return true; } bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { // Dex files are required to be 4 byte aligned. - size_t original_offset = size_; - size_t offset = RoundUp(original_offset, 4); - size_dex_file_alignment_ += offset - original_offset; + size_t initial_offset = kIsVdexEnabled ? vdex_size_ : oat_size_; + size_t start_offset = RoundUp(initial_offset, 4); + size_t file_offset = kIsVdexEnabled ? start_offset : (oat_data_offset_ + start_offset); + size_dex_file_alignment_ += start_offset - initial_offset; // Seek to the start of the dex file and flush any pending operations in the stream. // Verify that, after flushing the stream, the file is at the same offset as the stream. - uint32_t start_offset = oat_data_offset_ + offset; - off_t actual_offset = out->Seek(start_offset, kSeekSet); - if (actual_offset != static_cast(start_offset)) { + off_t actual_offset = out->Seek(file_offset, kSeekSet); + if (actual_offset != static_cast(file_offset)) { PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset - << " Expected: " << start_offset + << " Expected: " << file_offset << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } @@ -1904,24 +1944,28 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex return false; } actual_offset = lseek(file->Fd(), 0, SEEK_CUR); - if (actual_offset != static_cast(start_offset)) { + if (actual_offset != static_cast(file_offset)) { PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset - << " Expected: " << start_offset + << " Expected: " << file_offset << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - size_ = offset; - oat_dex_file->dex_file_offset_ = offset; + if (kIsVdexEnabled) { + vdex_size_ = start_offset; + } else { + oat_size_ = start_offset; + } + oat_dex_file->dex_file_offset_ = start_offset; return true; } -bool OatWriter::WriteDexFile(OutputStream* rodata, +bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file) { - size_t start_offset = oat_data_offset_ + size_; - DCHECK_EQ(static_cast(start_offset), rodata->Seek(0, kSeekCurrent)); + size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; + DCHECK_EQ(static_cast(start_offset), out->Seek(0, kSeekCurrent)); // Extract the dex file and get the extracted size. std::string error_msg; @@ -1984,13 +2028,13 @@ bool OatWriter::WriteDexFile(OutputStream* rodata, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - actual_offset = rodata->Seek(end_offset, kSeekSet); + actual_offset = out->Seek(end_offset, kSeekSet); if (actual_offset != static_cast(end_offset)) { PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); return false; } - if (!rodata->Flush()) { + if (!out->Flush()) { PLOG(ERROR) << "Failed to flush stream after seeking over dex file." << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; @@ -2000,7 +2044,8 @@ bool OatWriter::WriteDexFile(OutputStream* rodata, if (extracted_size > oat_dex_file->dex_file_size_) { if (file->SetLength(end_offset) != 0) { PLOG(ERROR) << "Failed to truncate excessive dex file length." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + << " File: " << oat_dex_file->GetLocation() + << " Output: " << file->GetPath(); return false; } } @@ -2008,12 +2053,12 @@ bool OatWriter::WriteDexFile(OutputStream* rodata, return true; } -bool OatWriter::WriteDexFile(OutputStream* rodata, +bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, File* dex_file) { - size_t start_offset = oat_data_offset_ + size_; - DCHECK_EQ(static_cast(start_offset), rodata->Seek(0, kSeekCurrent)); + size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; + DCHECK_EQ(static_cast(start_offset), out->Seek(0, kSeekCurrent)); off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET); if (input_offset != static_cast(0)) { @@ -2047,13 +2092,13 @@ bool OatWriter::WriteDexFile(OutputStream* rodata, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - actual_offset = rodata->Seek(end_offset, kSeekSet); + actual_offset = out->Seek(end_offset, kSeekSet); if (actual_offset != static_cast(end_offset)) { PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); return false; } - if (!rodata->Flush()) { + if (!out->Flush()) { PLOG(ERROR) << "Failed to flush stream after seeking over dex file." << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; @@ -2062,7 +2107,7 @@ bool OatWriter::WriteDexFile(OutputStream* rodata, return true; } -bool OatWriter::WriteDexFile(OutputStream* rodata, +bool OatWriter::WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, const uint8_t* dex_file) { // Note: The raw data has already been checked to contain the header @@ -2071,12 +2116,12 @@ bool OatWriter::WriteDexFile(OutputStream* rodata, DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation())); const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file); - if (!rodata->WriteFully(dex_file, header->file_size_)) { + if (!out->WriteFully(dex_file, header->file_size_)) { PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation() - << " to " << rodata->GetLocation(); + << " to " << out->GetLocation(); return false; } - if (!rodata->Flush()) { + if (!out->Flush()) { PLOG(ERROR) << "Failed to flush stream after writing dex file." << " File: " << oat_dex_file->GetLocation(); return false; @@ -2146,16 +2191,18 @@ bool OatWriter::OpenDexFiles( } size_t map_offset = oat_dex_files_[0].dex_file_offset_; - size_t length = size_ - map_offset; + size_t length = kIsVdexEnabled ? (vdex_size_ - map_offset) : (oat_size_ - map_offset); + std::string error_msg; - std::unique_ptr dex_files_map(MemMap::MapFile(length, - PROT_READ | PROT_WRITE, - MAP_SHARED, - file->Fd(), - oat_data_offset_ + map_offset, - /* low_4gb */ false, - file->GetPath().c_str(), - &error_msg)); + std::unique_ptr dex_files_map(MemMap::MapFile( + length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + file->Fd(), + kIsVdexEnabled ? map_offset : (oat_data_offset_ + map_offset), + /* low_4gb */ false, + file->GetPath().c_str(), + &error_msg)); if (dex_files_map == nullptr) { LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath() << " error: " << error_msg; @@ -2210,10 +2257,18 @@ bool OatWriter::OpenDexFiles( } bool OatWriter::WriteTypeLookupTables( - OutputStream* rodata, + OutputStream* oat_rodata, const std::vector>& opened_dex_files) { TimingLogger::ScopedTiming split("WriteTypeLookupTables", timings_); + uint32_t expected_offset = oat_data_offset_ + oat_size_; + off_t actual_offset = oat_rodata->Seek(expected_offset, kSeekSet); + if (static_cast(actual_offset) != expected_offset) { + PLOG(ERROR) << "Failed to seek to TypeLookupTable section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << oat_rodata->GetLocation(); + return false; + } + DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size()); for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) { OatDexFile* oat_dex_file = &oat_dex_files_[i]; @@ -2235,41 +2290,58 @@ bool OatWriter::WriteTypeLookupTables( TypeLookupTable* table = opened_dex_files[i]->GetTypeLookupTable(); // Type tables are required to be 4 byte aligned. - size_t original_offset = size_; - size_t rodata_offset = RoundUp(original_offset, 4); - size_t padding_size = rodata_offset - original_offset; + size_t initial_offset = oat_size_; + size_t rodata_offset = RoundUp(initial_offset, 4); + size_t padding_size = rodata_offset - initial_offset; if (padding_size != 0u) { std::vector buffer(padding_size, 0u); - if (!rodata->WriteFully(buffer.data(), padding_size)) { + if (!oat_rodata->WriteFully(buffer.data(), padding_size)) { PLOG(ERROR) << "Failed to write lookup table alignment padding." << " File: " << oat_dex_file->GetLocation() - << " Output: " << rodata->GetLocation(); + << " Output: " << oat_rodata->GetLocation(); return false; } } DCHECK_EQ(oat_data_offset_ + rodata_offset, - static_cast(rodata->Seek(0u, kSeekCurrent))); + static_cast(oat_rodata->Seek(0u, kSeekCurrent))); DCHECK_EQ(table_size, table->RawDataLength()); - if (!rodata->WriteFully(table->RawData(), table_size)) { + if (!oat_rodata->WriteFully(table->RawData(), table_size)) { PLOG(ERROR) << "Failed to write lookup table." << " File: " << oat_dex_file->GetLocation() - << " Output: " << rodata->GetLocation(); + << " Output: " << oat_rodata->GetLocation(); return false; } oat_dex_file->lookup_table_offset_ = rodata_offset; - size_ += padding_size + table_size; + oat_size_ += padding_size + table_size; size_oat_lookup_table_ += table_size; size_oat_lookup_table_alignment_ += padding_size; } - if (!rodata->Flush()) { + if (!oat_rodata->Flush()) { PLOG(ERROR) << "Failed to flush stream after writing type lookup tables." - << " File: " << rodata->GetLocation(); + << " File: " << oat_rodata->GetLocation(); + return false; + } + + return true; +} + +bool OatWriter::WriteVdexHeader(OutputStream* vdex_out) { + off_t actual_offset = vdex_out->Seek(0, kSeekSet); + if (actual_offset != 0) { + PLOG(ERROR) << "Failed to seek to the beginning of vdex file. Actual: " << actual_offset + << " File: " << vdex_out->GetLocation(); + return false; + } + + VdexFile::Header vdex_header; + if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) { + PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation(); return false; } @@ -2329,11 +2401,11 @@ void OatWriter::OatDexFile::ReserveClassOffsets(OatWriter* oat_writer) { DCHECK_EQ(class_offsets_offset_, 0u); if (!class_offsets_.empty()) { // Class offsets are required to be 4 byte aligned. - size_t original_offset = oat_writer->size_; - size_t offset = RoundUp(original_offset, 4); - oat_writer->size_oat_class_offsets_alignment_ += offset - original_offset; + size_t initial_offset = oat_writer->oat_size_; + size_t offset = RoundUp(initial_offset, 4); + oat_writer->size_oat_class_offsets_alignment_ += offset - initial_offset; class_offsets_offset_ = offset; - oat_writer->size_ = offset + GetClassOffsetsRawSize(); + oat_writer->oat_size_ = offset + GetClassOffsetsRawSize(); } } diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 93e2e440b..77525f1a3 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -57,11 +57,6 @@ class MultiOatRelativePatcher; // ... // OatDexFile[D] // -// Dex[0] one variable sized DexFile for each OatDexFile. -// Dex[1] these are literal copies of the input .dex files. -// ... -// Dex[D] -// // TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile. // TypeLookupTable[1] // ... @@ -142,11 +137,12 @@ class OatWriter { CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); dchecked_vector GetSourceLocations() const; - // Write raw dex files to the .rodata section and open them from the oat file. The verify - // setting dictates whether the dex file verifier should check the dex files. This is generally - // the case, and should only be false for tests. - bool WriteAndOpenDexFiles(OutputStream* rodata, - File* file, + // Write raw dex files to the vdex file, mmap the file and open the dex files from it. + // Supporting data structures are written into the .rodata section of the oat file. + // The `verify` setting dictates whether the dex file verifier should check the dex files. + // This is generally the case, and should only be false for tests. + bool WriteAndOpenDexFiles(File* vdex_file, + OutputStream* oat_rodata, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, SafeMap* key_value_store, @@ -183,8 +179,8 @@ class OatWriter { return *oat_header_; } - size_t GetSize() const { - return size_; + size_t GetOatSize() const { + return oat_size_; } size_t GetBssSize() const { @@ -236,6 +232,25 @@ class OatWriter { // with a given DexMethodVisitor. bool VisitDexMethods(DexMethodVisitor* visitor); + bool WriteVdexHeader(OutputStream* vdex_out); + + bool WriteDexFiles(OutputStream* out, File* file); + bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); + bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); + bool WriteDexFile(OutputStream* out, + File* file, + OatDexFile* oat_dex_file, + ZipEntry* dex_file); + bool WriteDexFile(OutputStream* out, + File* file, + OatDexFile* oat_dex_file, + File* dex_file); + bool WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, const uint8_t* dex_file); + bool OpenDexFiles(File* file, + bool verify, + /*out*/ std::unique_ptr* opened_dex_files_map, + /*out*/ std::vector>* opened_dex_files); + size_t InitOatHeader(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t num_dex_files, @@ -253,20 +268,10 @@ class OatWriter { size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); bool RecordOatDataOffset(OutputStream* out); - bool ReadDexFileHeader(File* file, OatDexFile* oat_dex_file); + bool ReadDexFileHeader(File* oat_file, OatDexFile* oat_dex_file); bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); - bool WriteDexFiles(OutputStream* rodata, File* file); - bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file); - bool SeekToDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file); - bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file); - bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, File* dex_file); - bool WriteDexFile(OutputStream* rodata, OatDexFile* oat_dex_file, const uint8_t* dex_file); - bool WriteOatDexFiles(OutputStream* rodata); - bool OpenDexFiles(File* file, - bool verify, - /*out*/ std::unique_ptr* opened_dex_files_map, - /*out*/ std::vector>* opened_dex_files); - bool WriteTypeLookupTables(OutputStream* rodata, + bool WriteOatDexFiles(OutputStream* oat_rodata); + bool WriteTypeLookupTables(OutputStream* oat_rodata, const std::vector>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); void SetMultiOatRelativePatcherAdjustment(); @@ -300,8 +305,14 @@ class OatWriter { // note OatFile does not take ownership of the DexFiles const std::vector* dex_files_; + // Size required for Vdex data structures. + size_t vdex_size_; + + // Offset of section holding Dex files inside Vdex. + size_t vdex_dex_files_offset_; + // Size required for Oat data structures. - size_t size_; + size_t oat_size_; // The size of the required .bss section holding the DexCache data. size_t bss_size_; @@ -324,6 +335,7 @@ class OatWriter { std::unique_ptr> quick_to_interpreter_bridge_; // output stats + uint32_t size_vdex_header_; uint32_t size_dex_file_alignment_; uint32_t size_executable_offset_alignment_; uint32_t size_oat_header_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index febfb6309..1dd91321c 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -507,6 +507,7 @@ class Dex2Oat FINAL { thread_count_(sysconf(_SC_NPROCESSORS_CONF)), start_ns_(NanoTime()), oat_fd_(-1), + vdex_fd_(-1), zip_fd_(-1), image_base_(0U), image_classes_zip_filename_(nullptr), @@ -557,6 +558,9 @@ class Dex2Oat FINAL { for (std::unique_ptr& map : opened_dex_files_maps_) { map.release(); } + for (std::unique_ptr& vdex_file : vdex_files_) { + vdex_file.release(); + } for (std::unique_ptr& oat_file : oat_files_) { oat_file.release(); } @@ -578,6 +582,10 @@ class Dex2Oat FINAL { ParseUintOption(option, "--zip-fd", &zip_fd_, Usage); } + void ParseVdexFd(const StringPiece& option) { + ParseUintOption(option, "--vdex-fd", &vdex_fd_, Usage); + } + void ParseOatFd(const StringPiece& option) { ParseUintOption(option, "--oat-fd", &oat_fd_, Usage); } @@ -693,6 +701,11 @@ class Dex2Oat FINAL { Usage("--oat-file should not be used with --oat-fd"); } + if ((vdex_fd_ == -1) != (oat_fd_ == -1)) { + Usage("VDEX and OAT output must be specified either with one --oat-filename " + "or with --oat-fd and --vdex-fd file descriptors"); + } + if (!parser_options->oat_symbols.empty() && oat_fd_ != -1) { Usage("--oat-symbols should not be used with --oat-fd"); } @@ -701,6 +714,10 @@ class Dex2Oat FINAL { Usage("--oat-symbols should not be used with --host"); } + if (vdex_fd_ != -1 && !image_filenames_.empty()) { + Usage("--vdex-fd should not be used with --image"); + } + if (oat_fd_ != -1 && !image_filenames_.empty()) { Usage("--oat-fd should not be used with --image"); } @@ -1074,20 +1091,22 @@ class Dex2Oat FINAL { ParseZipFd(option); } else if (option.starts_with("--zip-location=")) { zip_location_ = option.substr(strlen("--zip-location=")).data(); + } else if (option.starts_with("--vdex-fd=")) { + ParseVdexFd(option); } else if (option.starts_with("--oat-file=")) { oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data()); } else if (option.starts_with("--oat-symbols=")) { parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data()); } else if (option.starts_with("--oat-fd=")) { ParseOatFd(option); + } else if (option.starts_with("--oat-location=")) { + oat_location_ = option.substr(strlen("--oat-location=")).data(); } else if (option == "--watch-dog") { parser_options->watch_dog_enabled = true; } else if (option == "--no-watch-dog") { parser_options->watch_dog_enabled = false; } else if (option.starts_with("-j")) { ParseJ(option); - } else if (option.starts_with("--oat-location=")) { - oat_location_ = option.substr(strlen("--oat-location=")).data(); } else if (option.starts_with("--image=")) { image_filenames_.push_back(option.substr(strlen("--image=")).data()); } else if (option.starts_with("--image-classes=")) { @@ -1199,41 +1218,66 @@ class Dex2Oat FINAL { ExpandOatAndImageFilenames(); } - bool create_file = oat_fd_ == -1; // as opposed to using open file descriptor - if (create_file) { + // OAT and VDEX file handling + + if (oat_fd_ == -1) { + DCHECK(!oat_filenames_.empty()); for (const char* oat_filename : oat_filenames_) { std::unique_ptr oat_file(OS::CreateEmptyFile(oat_filename)); if (oat_file.get() == nullptr) { PLOG(ERROR) << "Failed to create oat file: " << oat_filename; return false; } - if (create_file && fchmod(oat_file->Fd(), 0644) != 0) { + if (fchmod(oat_file->Fd(), 0644) != 0) { PLOG(ERROR) << "Failed to make oat file world readable: " << oat_filename; oat_file->Erase(); return false; } oat_files_.push_back(std::move(oat_file)); + + DCHECK_EQ(vdex_fd_, -1); + std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex"); + std::unique_ptr vdex_file(OS::CreateEmptyFile(vdex_filename.c_str())); + if (vdex_file.get() == nullptr) { + PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename; + return false; + } + if (fchmod(vdex_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename; + vdex_file->Erase(); + return false; + } + vdex_files_.push_back(std::move(vdex_file)); } } else { - std::unique_ptr oat_file(new File(oat_fd_, oat_location_, true)); + std::unique_ptr oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true)); + if (oat_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create oat file: " << oat_location_; + return false; + } oat_file->DisableAutoClose(); if (oat_file->SetLength(0) != 0) { PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed."; } - if (oat_file.get() == nullptr) { - PLOG(ERROR) << "Failed to create oat file: " << oat_location_; + oat_files_.push_back(std::move(oat_file)); + + DCHECK_NE(vdex_fd_, -1); + std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex"); + std::unique_ptr vdex_file(new File(vdex_fd_, vdex_location, /* check_usage */ true)); + if (vdex_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create vdex file: " << vdex_location; return false; } - if (create_file && fchmod(oat_file->Fd(), 0644) != 0) { - PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_; - oat_file->Erase(); - return false; + vdex_file->DisableAutoClose(); + if (vdex_file->SetLength(0) != 0) { + PLOG(WARNING) << "Truncating vdex file " << vdex_location << " failed."; } + vdex_files_.push_back(std::move(vdex_file)); + oat_filenames_.push_back(oat_location_.c_str()); - oat_files_.push_back(std::move(oat_file)); } - // Swap file handling. + // Swap file handling // // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file // that we can use for swap. @@ -1256,11 +1300,14 @@ class Dex2Oat FINAL { return true; } - void EraseOatFiles() { - for (size_t i = 0; i < oat_files_.size(); ++i) { - DCHECK(oat_files_[i].get() != nullptr); - oat_files_[i]->Erase(); - oat_files_[i].reset(); + void EraseOutputFiles() { + for (auto& files : { &vdex_files_, &oat_files_ }) { + for (size_t i = 0; i < files->size(); ++i) { + if ((*files)[i].get() != nullptr) { + (*files)[i]->Erase(); + (*files)[i].reset(); + } + } } } @@ -1399,14 +1446,15 @@ class Dex2Oat FINAL { // Unzip or copy dex files straight to the oat file. std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; - if (!oat_writers_[i]->WriteAndOpenDexFiles(rodata_.back(), - oat_files_[i].get(), - instruction_set_, - instruction_set_features_.get(), - key_value_store_.get(), - /* verify */ true, - &opened_dex_files_map, - &opened_dex_files)) { + if (!oat_writers_[i]->WriteAndOpenDexFiles( + kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(), + rodata_.back(), + instruction_set_, + instruction_set_features_.get(), + key_value_store_.get(), + /* verify */ true, + &opened_dex_files_map, + &opened_dex_files)) { return false; } dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); @@ -1652,7 +1700,7 @@ class Dex2Oat FINAL { // ImageWriter, if necessary. // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure // case (when the file will be explicitly erased). - bool WriteOatFiles() { + bool WriteOutputFiles() { TimingLogger::ScopedTiming t("dex2oat Oat", timings_); // Sync the data to the file, in case we did dex2dex transformations. @@ -1709,7 +1757,7 @@ class Dex2Oat FINAL { oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files, &patcher); size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetSize() - rodata_size; + size_t text_size = oat_writer->GetOatSize() - rodata_size; elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize()); if (IsImage()) { @@ -1719,7 +1767,7 @@ class Dex2Oat FINAL { image_writer_->UpdateOatFileLayout(i, elf_writer->GetLoadedSize(), oat_writer->GetOatDataOffset(), - oat_writer->GetSize()); + oat_writer->GetOatSize()); } } @@ -1774,12 +1822,8 @@ class Dex2Oat FINAL { return false; } - // Flush the oat file. - if (oat_files_[i] != nullptr) { - if (oat_files_[i]->Flush() != 0) { - PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i]; - return false; - } + if (!FlushOutputFile(&vdex_files_[i]) || !FlushOutputFile(&oat_files_[i])) { + return false; } VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i]; @@ -1812,7 +1856,7 @@ class Dex2Oat FINAL { if (strcmp(oat_unstripped_[i], oat_filenames_[i]) != 0) { // If the oat file is still open, flush it. if (oat_files_[i].get() != nullptr && oat_files_[i]->IsOpened()) { - if (!FlushCloseOatFile(i)) { + if (!FlushCloseOutputFile(&oat_files_[i])) { return false; } } @@ -1840,35 +1884,45 @@ class Dex2Oat FINAL { return true; } - bool FlushOatFiles() { - TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_); - for (size_t i = 0; i < oat_files_.size(); ++i) { - if (oat_files_[i].get() != nullptr) { - if (oat_files_[i]->Flush() != 0) { - PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i]; - oat_files_[i]->Erase(); - return false; - } + bool FlushOutputFile(std::unique_ptr* file) { + if (file->get() != nullptr) { + if (file->get()->Flush() != 0) { + PLOG(ERROR) << "Failed to flush output file: " << file->get()->GetPath(); + return false; } } return true; } - bool FlushCloseOatFile(size_t i) { - if (oat_files_[i].get() != nullptr) { - std::unique_ptr tmp(oat_files_[i].release()); + bool FlushCloseOutputFile(std::unique_ptr* file) { + if (file->get() != nullptr) { + std::unique_ptr tmp(file->release()); if (tmp->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close oat file: " << oat_filenames_[i]; + PLOG(ERROR) << "Failed to flush and close output file: " << tmp->GetPath(); return false; } } return true; } - bool FlushCloseOatFiles() { + bool FlushOutputFiles() { + TimingLogger::ScopedTiming t2("dex2oat Flush Output Files", timings_); + for (auto& files : { &vdex_files_, &oat_files_ }) { + for (size_t i = 0; i < files->size(); ++i) { + if (!FlushOutputFile(&(*files)[i])) { + return false; + } + } + } + return true; + } + + bool FlushCloseOutputFiles() { bool result = true; - for (size_t i = 0; i < oat_files_.size(); ++i) { - result &= FlushCloseOatFile(i); + for (auto& files : { &vdex_files_, &oat_files_ }) { + for (size_t i = 0; i < files->size(); ++i) { + result &= FlushCloseOutputFile(&(*files)[i]); + } } return result; } @@ -2503,10 +2557,12 @@ class Dex2Oat FINAL { uint64_t start_ns_; std::unique_ptr watchdog_; std::vector> oat_files_; + std::vector> vdex_files_; std::string oat_location_; std::vector oat_filenames_; std::vector oat_unstripped_; int oat_fd_; + int vdex_fd_; std::vector dex_filenames_; std::vector dex_locations_; int zip_fd_; @@ -2603,8 +2659,8 @@ static int CompileImage(Dex2Oat& dex2oat) { dex2oat.LoadClassProfileDescriptors(); dex2oat.Compile(); - if (!dex2oat.WriteOatFiles()) { - dex2oat.EraseOatFiles(); + if (!dex2oat.WriteOutputFiles()) { + dex2oat.EraseOutputFiles(); return EXIT_FAILURE; } @@ -2612,10 +2668,11 @@ static int CompileImage(Dex2Oat& dex2oat) { // unstripped name. Do not close the file if we are compiling the image with an oat fd since the // image writer will require this fd to generate the image. if (dex2oat.ShouldKeepOatFileOpen()) { - if (!dex2oat.FlushOatFiles()) { + if (!dex2oat.FlushOutputFiles()) { + dex2oat.EraseOutputFiles(); return EXIT_FAILURE; } - } else if (!dex2oat.FlushCloseOatFiles()) { + } else if (!dex2oat.FlushCloseOutputFiles()) { return EXIT_FAILURE; } @@ -2636,7 +2693,7 @@ static int CompileImage(Dex2Oat& dex2oat) { } // FlushClose again, as stripping might have re-opened the oat files. - if (!dex2oat.FlushCloseOatFiles()) { + if (!dex2oat.FlushCloseOutputFiles()) { return EXIT_FAILURE; } @@ -2647,8 +2704,8 @@ static int CompileImage(Dex2Oat& dex2oat) { static int CompileApp(Dex2Oat& dex2oat) { dex2oat.Compile(); - if (!dex2oat.WriteOatFiles()) { - dex2oat.EraseOatFiles(); + if (!dex2oat.WriteOutputFiles()) { + dex2oat.EraseOutputFiles(); return EXIT_FAILURE; } @@ -2657,7 +2714,7 @@ static int CompileApp(Dex2Oat& dex2oat) { // When given --host, finish early without stripping. if (dex2oat.IsHost()) { - if (!dex2oat.FlushCloseOatFiles()) { + if (!dex2oat.FlushCloseOutputFiles()) { return EXIT_FAILURE; } @@ -2672,7 +2729,7 @@ static int CompileApp(Dex2Oat& dex2oat) { } // Flush and close the files. - if (!dex2oat.FlushCloseOatFiles()) { + if (!dex2oat.FlushCloseOutputFiles()) { return EXIT_FAILURE; } @@ -2721,7 +2778,7 @@ static int dex2oat(int argc, char** argv) { } if (!dex2oat->Setup()) { - dex2oat->EraseOatFiles(); + dex2oat->EraseOutputFiles(); return EXIT_FAILURE; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index c87a18b4c..db6a70962 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -719,10 +719,12 @@ class OatDumper { os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str()); os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum()); - // Print embedded dex file data range. const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin(); + const uint8_t* const vdex_file_begin = oat_dex_file.GetOatFile()->DexBegin(); + + // Print data range of the dex file embedded inside the corresponding vdex file. const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer(); - uint32_t dex_offset = dchecked_integral_cast(dex_file_pointer - oat_file_begin); + uint32_t dex_offset = dchecked_integral_cast(dex_file_pointer - vdex_file_begin); os << StringPrintf("dex-file: 0x%08x..0x%08x\n", dex_offset, dchecked_integral_cast(dex_offset + oat_dex_file.FileSize() - 1)); diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b7ce02c50..524001190 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -31,6 +31,7 @@ #include "base/stringpiece.h" #include "base/stringprintf.h" #include "base/unix_file/fd_file.h" +#include "base/unix_file/random_access_file_utils.h" #include "elf_utils.h" #include "elf_file.h" #include "elf_file_impl.h" @@ -151,6 +152,28 @@ static bool FinishFile(File* file, bool close) { } } +static bool SymlinkFile(const std::string& input_filename, const std::string& output_filename) { + if (input_filename == output_filename) { + // Input and output are the same, nothing to do. + return true; + } + + // Unlink the original filename, since we are overwriting it. + unlink(output_filename.c_str()); + + // Create a symlink from the source file to the target path. + if (symlink(input_filename.c_str(), output_filename.c_str()) < 0) { + PLOG(ERROR) << "Failed to create symlink " << output_filename << " -> " << input_filename; + return false; + } + + if (kIsDebugBuild) { + LOG(INFO) << "Created symlink " << output_filename << " -> " << input_filename; + } + + return true; +} + bool PatchOat::Patch(const std::string& image_location, off_t delta, const std::string& output_directory, @@ -230,9 +253,13 @@ bool PatchOat::Patch(const std::string& image_location, space_to_memmap_map.emplace(space, std::move(image)); } + // Do a first pass over the image spaces. Symlink PIC oat and vdex files, and + // prepare PatchOat instances for the rest. for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; std::string input_image_filename = space->GetImageFilename(); + std::string input_vdex_filename = + ImageHeader::GetVdexLocationFromImageLocation(input_image_filename); std::string input_oat_filename = ImageHeader::GetOatLocationFromImageLocation(input_image_filename); std::unique_ptr input_oat_file(OS::OpenFileForReading(input_oat_filename.c_str())); @@ -261,13 +288,16 @@ bool PatchOat::Patch(const std::string& image_location, std::string output_image_filename = output_directory + (StartsWith(converted_image_filename, "/") ? "" : "/") + converted_image_filename; + std::string output_vdex_filename = + ImageHeader::GetVdexLocationFromImageLocation(output_image_filename); std::string output_oat_filename = ImageHeader::GetOatLocationFromImageLocation(output_image_filename); if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(), output_oat_filename, false, - true)) { + true) || + !SymlinkFile(input_vdex_filename, output_vdex_filename)) { // Errors already logged by above call. return false; } @@ -301,9 +331,13 @@ bool PatchOat::Patch(const std::string& image_location, space_to_skip_patching_map.emplace(space, skip_patching_oat); } + // Do a second pass over the image spaces. Patch image files, non-PIC oat files + // and symlink their corresponding vdex files. for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; std::string input_image_filename = space->GetImageFilename(); + std::string input_vdex_filename = + ImageHeader::GetVdexLocationFromImageLocation(input_image_filename); t.NewTiming("Writing files"); std::string converted_image_filename = space->GetImageLocation(); @@ -329,8 +363,11 @@ bool PatchOat::Patch(const std::string& image_location, bool skip_patching_oat = space_to_skip_patching_map.find(space)->second; if (!skip_patching_oat) { + std::string output_vdex_filename = + ImageHeader::GetVdexLocationFromImageLocation(output_image_filename); std::string output_oat_filename = ImageHeader::GetOatLocationFromImageLocation(output_image_filename); + std::unique_ptr output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out)); if (output_oat_file.get() == nullptr) { @@ -339,6 +376,9 @@ bool PatchOat::Patch(const std::string& image_location, } success = p.WriteElf(output_oat_file.get()); success = FinishFile(output_oat_file.get(), success); + if (success) { + success = SymlinkFile(input_vdex_filename, output_vdex_filename); + } if (!success) { return false; } @@ -921,6 +961,9 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --input-oat-fd=: Specifies the file-descriptor of the oat file"); UsageError(" to be patched."); UsageError(""); + UsageError(" --input-vdex-fd=: Specifies the file-descriptor of the vdex file"); + UsageError(" associated with the oat file."); + UsageError(""); UsageError(" --input-oat-location=: Specifies the 'location' to read the patched"); UsageError(" oat file from. If used one must also supply the --instruction-set"); UsageError(""); @@ -932,7 +975,10 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" file to."); UsageError(""); UsageError(" --output-oat-fd=: Specifies the file-descriptor to write the"); - UsageError(" the patched oat file to."); + UsageError(" patched oat file to."); + UsageError(""); + UsageError(" --output-vdex-fd=: Specifies the file-descriptor to copy the"); + UsageError(" the vdex file associated with the patch oat file to."); UsageError(""); UsageError(" --output-image-file=: Specifies the exact file to write the patched"); UsageError(" image file to."); @@ -1029,10 +1075,12 @@ static int patchoat_oat(TimingLogger& timings, off_t base_delta, bool base_delta_set, int input_oat_fd, + int input_vdex_fd, const std::string& input_oat_location, std::string input_oat_filename, bool have_input_oat, int output_oat_fd, + int output_vdex_fd, std::string output_oat_filename, bool have_output_oat, bool lock_output, @@ -1062,6 +1110,12 @@ static int patchoat_oat(TimingLogger& timings, } } + if ((input_oat_fd == -1) != (input_vdex_fd == -1)) { + Usage("Either both input oat and vdex have to be passed as file descriptors or none of them"); + } else if ((output_oat_fd == -1) != (output_vdex_fd == -1)) { + Usage("Either both output oat and vdex have to be passed as file descriptors or none of them"); + } + bool match_delta = false; if (!patched_image_location.empty()) { std::string system_filename; @@ -1102,8 +1156,24 @@ static int patchoat_oat(TimingLogger& timings, Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize); } + // We can symlink VDEX only if we have both input and output specified as filenames. + // Store that piece of information before we possibly create bogus filenames for + // files passed as file descriptors. + bool symlink_vdex = !input_oat_filename.empty() && !output_oat_filename.empty(); + + // Infer names of VDEX files. + std::string input_vdex_filename; + std::string output_vdex_filename; + if (!input_oat_filename.empty()) { + input_vdex_filename = ReplaceFileExtension(input_oat_filename, "vdex"); + } + if (!output_oat_filename.empty()) { + output_vdex_filename = ReplaceFileExtension(output_oat_filename, "vdex"); + } + // Do we need to cleanup output files if we fail? bool new_oat_out = false; + bool new_vdex_out = false; std::unique_ptr input_oat; std::unique_ptr output_oat; @@ -1162,13 +1232,52 @@ static int patchoat_oat(TimingLogger& timings, } } + // Open VDEX files if we are not symlinking them. + std::unique_ptr input_vdex; + std::unique_ptr output_vdex; + if (symlink_vdex) { + new_vdex_out = !OS::FileExists(output_vdex_filename.c_str()); + } else { + if (input_vdex_fd != -1) { + input_vdex.reset(new File(input_vdex_fd, input_vdex_filename, true)); + if (input_vdex == nullptr) { + // Unlikely, but ensure exhaustive logging in non-0 exit code case + LOG(ERROR) << "Failed to open input vdex file by its FD" << input_vdex_fd; + } + } else { + input_vdex.reset(OS::OpenFileForReading(input_vdex_filename.c_str())); + if (input_vdex == nullptr) { + PLOG(ERROR) << "Failed to open input vdex file " << input_vdex_filename; + return EXIT_FAILURE; + } + } + if (output_vdex_fd != -1) { + output_vdex.reset(new File(output_vdex_fd, output_vdex_filename, true)); + if (output_vdex == nullptr) { + // Unlikely, but ensure exhaustive logging in non-0 exit code case + LOG(ERROR) << "Failed to open output vdex file by its FD" << output_vdex_fd; + } + } else { + output_vdex.reset(CreateOrOpen(output_vdex_filename.c_str(), &new_vdex_out)); + if (output_vdex == nullptr) { + PLOG(ERROR) << "Failed to open output vdex file " << output_vdex_filename; + return EXIT_FAILURE; + } + } + } + // TODO: get rid of this. - auto cleanup = [&output_oat_filename, &new_oat_out](bool success) { + auto cleanup = [&output_oat_filename, &output_vdex_filename, &new_oat_out, &new_vdex_out] + (bool success) { if (!success) { if (new_oat_out) { CHECK(!output_oat_filename.empty()); unlink(output_oat_filename.c_str()); } + if (new_vdex_out) { + CHECK(!output_vdex_filename.empty()); + unlink(output_vdex_filename.c_str()); + } } if (kIsDebugBuild) { @@ -1220,6 +1329,14 @@ static int patchoat_oat(TimingLogger& timings, new_oat_out); ret = FinishFile(output_oat.get(), ret); + if (ret) { + if (symlink_vdex) { + ret = SymlinkFile(input_vdex_filename, output_vdex_filename); + } else { + ret = unix_file::CopyFile(*input_vdex.get(), output_vdex.get()); + } + } + if (kIsDebugBuild) { LOG(INFO) << "Exiting with return ... " << ret; } @@ -1227,6 +1344,18 @@ static int patchoat_oat(TimingLogger& timings, return ret ? EXIT_SUCCESS : EXIT_FAILURE; } +static int ParseFd(const StringPiece& option, const char* cmdline_arg) { + int fd; + const char* fd_str = option.substr(strlen(cmdline_arg)).data(); + if (!ParseInt(fd_str, &fd)) { + Usage("Failed to parse %d argument '%s' as an integer", cmdline_arg, fd_str); + } + if (fd < 0) { + Usage("%s pass a negative value %d", cmdline_arg, fd); + } + return fd; +} + static int patchoat(int argc, char **argv) { InitLogging(argv); MemMap::Init(); @@ -1253,10 +1382,12 @@ static int patchoat(int argc, char **argv) { std::string input_oat_filename; std::string input_oat_location; int input_oat_fd = -1; + int input_vdex_fd = -1; bool have_input_oat = false; std::string input_image_location; std::string output_oat_filename; int output_oat_fd = -1; + int output_vdex_fd = -1; bool have_output_oat = false; std::string output_image_filename; off_t base_delta = 0; @@ -1296,13 +1427,9 @@ static int patchoat(int argc, char **argv) { Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); } have_input_oat = true; - const char* oat_fd_str = option.substr(strlen("--input-oat-fd=")).data(); - if (!ParseInt(oat_fd_str, &input_oat_fd)) { - Usage("Failed to parse --input-oat-fd argument '%s' as an integer", oat_fd_str); - } - if (input_oat_fd < 0) { - Usage("--input-oat-fd pass a negative value %d", input_oat_fd); - } + input_oat_fd = ParseFd(option, "--input-oat-fd="); + } else if (option.starts_with("--input-vdex-fd=")) { + input_vdex_fd = ParseFd(option, "--input-vdex-fd="); } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); } else if (option.starts_with("--output-oat-file=")) { @@ -1316,13 +1443,9 @@ static int patchoat(int argc, char **argv) { Usage("Only one of --output-oat-file, --output-oat-fd may be used."); } have_output_oat = true; - const char* oat_fd_str = option.substr(strlen("--output-oat-fd=")).data(); - if (!ParseInt(oat_fd_str, &output_oat_fd)) { - Usage("Failed to parse --output-oat-fd argument '%s' as an integer", oat_fd_str); - } - if (output_oat_fd < 0) { - Usage("--output-oat-fd pass a negative value %d", output_oat_fd); - } + output_oat_fd = ParseFd(option, "--output-oat-fd="); + } else if (option.starts_with("--output-vdex-fd=")) { + output_vdex_fd = ParseFd(option, "--output-vdex-fd="); } else if (option.starts_with("--output-image-file=")) { output_image_filename = option.substr(strlen("--output-image-file=")).data(); } else if (option.starts_with("--base-offset-delta=")) { @@ -1367,10 +1490,12 @@ static int patchoat(int argc, char **argv) { base_delta, base_delta_set, input_oat_fd, + input_vdex_fd, input_oat_location, input_oat_filename, have_input_oat, output_oat_fd, + output_vdex_fd, output_oat_filename, have_output_oat, lock_output, diff --git a/runtime/Android.bp b/runtime/Android.bp index 59e4a1581..a88450506 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -190,6 +190,7 @@ cc_defaults { "type_lookup_table.cc", "utf.cc", "utils.cc", + "vdex_file.cc", "verifier/instruction_flags.cc", "verifier/method_verifier.cc", "verifier/reg_type.cc", diff --git a/runtime/globals.h b/runtime/globals.h index aba566138..691bf55d4 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -166,6 +166,12 @@ static constexpr bool kDefaultMustRelocate = true; static constexpr bool kArm32QuickCodeUseSoftFloat = false; +#ifdef ART_ENABLE_VDEX +static constexpr bool kIsVdexEnabled = true; +#else +static constexpr bool kIsVdexEnabled = false; +#endif + } // namespace art #endif // ART_RUNTIME_GLOBALS_H_ diff --git a/runtime/image.h b/runtime/image.h index 3a4fa79bc..da9976a90 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -168,13 +168,11 @@ class PACKED(4) ImageHeader { } static std::string GetOatLocationFromImageLocation(const std::string& image) { - std::string oat_filename = image; - if (oat_filename.length() <= 3) { - oat_filename += ".oat"; - } else { - oat_filename.replace(oat_filename.length() - 3, 3, "oat"); - } - return oat_filename; + return GetLocationFromImageLocation(image, "oat"); + } + + static std::string GetVdexLocationFromImageLocation(const std::string& image) { + return GetLocationFromImageLocation(image, "vdex"); } enum ImageMethod { @@ -299,6 +297,17 @@ class PACKED(4) ImageHeader { static const uint8_t kImageMagic[4]; static const uint8_t kImageVersion[4]; + static std::string GetLocationFromImageLocation(const std::string& image, + const std::string& extension) { + std::string filename = image; + if (filename.length() <= 3) { + filename += "." + extension; + } else { + filename.replace(filename.length() - 3, 3, extension); + } + return filename; + } + uint8_t magic_[4]; uint8_t version_[4]; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 5752fd954..cbc5d3c20 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -83,7 +83,8 @@ class OatFileBase : public OatFile { virtual ~OatFileBase() {} template - static OatFileBase* OpenOatFile(const std::string& elf_filename, + static OatFileBase* OpenOatFile(const std::string& vdex_filename, + const std::string& elf_filename, const std::string& location, uint8_t* requested_base, uint8_t* oat_file_begin, @@ -101,6 +102,11 @@ class OatFileBase : public OatFile { virtual void PreLoad() = 0; + bool LoadVdex(const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg); + virtual bool Load(const std::string& elf_filename, uint8_t* oat_file_begin, bool writable, @@ -131,7 +137,8 @@ class OatFileBase : public OatFile { }; template -OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename, +OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, + const std::string& elf_filename, const std::string& location, uint8_t* requested_base, uint8_t* oat_file_begin, @@ -144,6 +151,10 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename, ret->PreLoad(); + if (kIsVdexEnabled && !ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { + return nullptr; + } + if (!ret->Load(elf_filename, oat_file_begin, writable, @@ -166,6 +177,20 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename, return ret.release(); } +bool OatFileBase::LoadVdex(const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg) { + vdex_.reset(VdexFile::Open(vdex_filename, writable, low_4gb, error_msg)); + if (vdex_.get() == nullptr) { + *error_msg = StringPrintf("Failed to load vdex file '%s' %s", + vdex_filename.c_str(), + error_msg->c_str()); + return false; + } + return true; +} + bool OatFileBase::ComputeFields(uint8_t* requested_base, const std::string& file_path, std::string* error_msg) { @@ -321,29 +346,29 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_location.c_str()); return false; } - if (UNLIKELY(dex_file_offset > Size())) { + if (UNLIKELY(dex_file_offset > DexSize())) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u > %zu", GetLocation().c_str(), i, dex_file_location.c_str(), dex_file_offset, - Size()); + DexSize()); return false; } - if (UNLIKELY(Size() - dex_file_offset < sizeof(DexFile::Header))) { + if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u of %zu but the size of dex file header is %zu", GetLocation().c_str(), i, dex_file_location.c_str(), dex_file_offset, - Size(), + DexSize(), sizeof(DexFile::Header)); return false; } - const uint8_t* dex_file_pointer = Begin() + dex_file_offset; + const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset; if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid " "dex file magic '%s'", @@ -363,7 +388,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } const DexFile::Header* header = reinterpret_cast(dex_file_pointer); - if (Size() - dex_file_offset < header->file_size_) { + if (DexSize() - dex_file_offset < header->file_size_) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u and size %u truncated at %zu", GetLocation().c_str(), @@ -371,7 +396,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_location.c_str(), dex_file_offset, header->file_size_, - Size()); + DexSize()); return false; } @@ -942,31 +967,37 @@ OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file, : nullptr; } -OatFile* OatFile::Open(const std::string& filename, - const std::string& location, +OatFile* OatFile::Open(const std::string& oat_filename, + const std::string& oat_location, uint8_t* requested_base, uint8_t* oat_file_begin, bool executable, bool low_4gb, const char* abs_dex_location, std::string* error_msg) { - ScopedTrace trace("Open oat file " + location); - CHECK(!filename.empty()) << location; - CheckLocation(location); + ScopedTrace trace("Open oat file " + oat_location); + CHECK(!oat_filename.empty()) << oat_location; + CheckLocation(oat_location); - // Check that the file even exists, fast-fail. - if (!OS::FileExists(filename.c_str())) { - *error_msg = StringPrintf("File %s does not exist.", filename.c_str()); + std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex"); + + // Check that the files even exist, fast-fail. + if (kIsVdexEnabled && !OS::FileExists(vdex_filename.c_str())) { + *error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str()); + return nullptr; + } else if (!OS::FileExists(oat_filename.c_str())) { + *error_msg = StringPrintf("File %s does not exist.", oat_filename.c_str()); return nullptr; } // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is // disabled. - OatFile* with_dlopen = OatFileBase::OpenOatFile(filename, - location, + OatFile* with_dlopen = OatFileBase::OpenOatFile(vdex_filename, + oat_filename, + oat_location, requested_base, oat_file_begin, - false, + false /* writable */, executable, low_4gb, abs_dex_location, @@ -975,7 +1006,7 @@ OatFile* OatFile::Open(const std::string& filename, return with_dlopen; } if (kPrintDlOpenErrorMessage) { - LOG(ERROR) << "Failed to dlopen: " << filename << " with error " << *error_msg; + LOG(ERROR) << "Failed to dlopen: " << oat_filename << " with error " << *error_msg; } // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: // @@ -990,11 +1021,12 @@ OatFile* OatFile::Open(const std::string& filename, // // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN. - OatFile* with_internal = OatFileBase::OpenOatFile(filename, - location, + OatFile* with_internal = OatFileBase::OpenOatFile(vdex_filename, + oat_filename, + oat_location, requested_base, oat_file_begin, - false, + false /* writable */, executable, low_4gb, abs_dex_location, @@ -1036,6 +1068,7 @@ OatFile* OatFile::OpenReadable(File* file, OatFile::OatFile(const std::string& location, bool is_executable) : location_(location), + vdex_(nullptr), begin_(nullptr), end_(nullptr), bss_begin_(nullptr), @@ -1071,6 +1104,14 @@ const uint8_t* OatFile::BssEnd() const { return bss_end_; } +const uint8_t* OatFile::DexBegin() const { + return kIsVdexEnabled ? vdex_->Begin() : Begin(); +} + +const uint8_t* OatFile::DexEnd() const { + return kIsVdexEnabled ? vdex_->End() : End(); +} + const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, std::string* error_msg) const { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index f5ab9dc2a..96e651e44 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -30,6 +30,7 @@ #include "oat.h" #include "os.h" #include "utils.h" +#include "vdex_file.h" namespace art { @@ -46,6 +47,14 @@ class DummyOatFile; } // namespace collector } // namespace gc +// Runtime representation of the OAT file format which holds compiler output. +// The class opens an OAT file from storage and maps it to memory, typically with +// dlopen and provides access to its internal data structures (see OatWriter for +// for more details about the OAT format). +// In the process of loading OAT, the class also loads the associated VDEX file +// with the input DEX files (see VdexFile for details about the VDEX format). +// The raw DEX data are accessible transparently through the OatDexFile objects. + class OatFile { public: // Special classpath that skips shared library check. @@ -240,12 +249,19 @@ class OatFile { return BssEnd() - BssBegin(); } + size_t DexSize() const { + return DexEnd() - DexBegin(); + } + const uint8_t* Begin() const; const uint8_t* End() const; const uint8_t* BssBegin() const; const uint8_t* BssEnd() const; + const uint8_t* DexBegin() const; + const uint8_t* DexEnd() const; + // Returns the absolute dex location for the encoded relative dex location. // // If not null, abs_dex_location is used to resolve the absolute dex @@ -279,6 +295,9 @@ class OatFile { // The image will embed this to link its associated oat file. const std::string location_; + // Pointer to the Vdex file with the Dex files for this Oat file. + std::unique_ptr vdex_; + // Pointer to OatHeader. const uint8_t* begin_; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index fe6332dee..415f991b4 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -345,17 +345,6 @@ CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() { return odex_.CompilerFilter(); } -static std::string ArtFileName(const OatFile* oat_file) { - const std::string oat_file_location = oat_file->GetLocation(); - // Replace extension with .art - const size_t last_ext = oat_file_location.find_last_of('.'); - if (last_ext == std::string::npos) { - LOG(ERROR) << "No extension in oat file " << oat_file_location; - return std::string(); - } - return oat_file_location.substr(0, last_ext) + ".art"; -} - const std::string* OatFileAssistant::OatFileName() { return oat_.Filename(); } @@ -565,6 +554,7 @@ OatFileAssistant::GenerateOatFile(std::string* error_msg) { return kUpdateNotAttempted; } const std::string& oat_file_name = *oat_.Filename(); + const std::string& vdex_file_name = ReplaceFileExtension(oat_file_name, "vdex"); // dex2oat ignores missing dex files and doesn't report an error. // Check explicitly here so we can detect the error properly. @@ -574,8 +564,22 @@ OatFileAssistant::GenerateOatFile(std::string* error_msg) { return kUpdateNotAttempted; } - std::unique_ptr oat_file; - oat_file.reset(OS::CreateEmptyFile(oat_file_name.c_str())); + std::unique_ptr vdex_file(OS::CreateEmptyFile(vdex_file_name.c_str())); + if (vdex_file.get() == nullptr) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the vdex file " + vdex_file_name + + " could not be opened."; + return kUpdateNotAttempted; + } + + if (fchmod(vdex_file->Fd(), 0644) != 0) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the vdex file " + vdex_file_name + + " could not be made world readable."; + return kUpdateNotAttempted; + } + + std::unique_ptr oat_file(OS::CreateEmptyFile(oat_file_name.c_str())); if (oat_file.get() == nullptr) { *error_msg = "Generation of oat file " + oat_file_name + " not attempted because the oat file could not be created."; @@ -591,17 +595,26 @@ OatFileAssistant::GenerateOatFile(std::string* error_msg) { std::vector args; args.push_back("--dex-file=" + dex_location_); + args.push_back("--vdex-fd=" + std::to_string(vdex_file->Fd())); args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); args.push_back("--oat-location=" + oat_file_name); if (!Dex2Oat(args, error_msg)) { - // Manually delete the file. This ensures there is no garbage left over if - // the process unexpectedly died. + // Manually delete the oat and vdex files. This ensures there is no garbage + // left over if the process unexpectedly died. + vdex_file->Erase(); + unlink(vdex_file_name.c_str()); oat_file->Erase(); unlink(oat_file_name.c_str()); return kUpdateFailed; } + if (vdex_file->FlushCloseOrErase() != 0) { + *error_msg = "Unable to close vdex file " + vdex_file_name; + unlink(vdex_file_name.c_str()); + return kUpdateFailed; + } + if (oat_file->FlushCloseOrErase() != 0) { *error_msg = "Unable to close oat file " + oat_file_name; unlink(oat_file_name.c_str()); @@ -830,7 +843,7 @@ uint32_t OatFileAssistant::GetCombinedImageChecksum() { std::unique_ptr OatFileAssistant::OpenImageSpace(const OatFile* oat_file) { DCHECK(oat_file != nullptr); - std::string art_file = ArtFileName(oat_file); + std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art"); if (art_file.empty()) { return nullptr; } diff --git a/runtime/utils.cc b/runtime/utils.cc index 313190c84..d48edcfa5 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1213,6 +1213,15 @@ bool FileExistsAndNotEmpty(const std::string& filename) { return buffer.st_size > 0; } +std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) { + const size_t last_ext = filename.find_last_of('.'); + if (last_ext == std::string::npos) { + return filename + "." + new_extension; + } else { + return filename.substr(0, last_ext + 1) + new_extension; + } +} + std::string PrettyDescriptor(Primitive::Type type) { return PrettyDescriptor(Primitive::Descriptor(type)); } diff --git a/runtime/utils.h b/runtime/utils.h index 958f0a35e..f3284e830 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -279,6 +279,13 @@ int ExecAndReturnCode(std::vector& arg_vector, std::string* error_m bool FileExists(const std::string& filename); bool FileExistsAndNotEmpty(const std::string& filename); +// Returns `filename` with the text after the last occurrence of '.' replaced with +// `extension`. If `filename` does not contain a period, returns a string containing `filename`, +// a period, and `new_extension`. +// Example: ReplaceFileExtension("foo.bar", "abc") == "foo.abc" +// ReplaceFileExtension("foo", "abc") == "foo.abc" +std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension); + class VoidFunctor { public: template diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc new file mode 100644 index 000000000..12bc45108 --- /dev/null +++ b/runtime/vdex_file.cc @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vdex_file.h" + +#include + +#include "base/logging.h" + +namespace art { + +constexpr uint8_t VdexFile::Header::kVdexMagic[4]; +constexpr uint8_t VdexFile::Header::kVdexVersion[4]; + +bool VdexFile::Header::IsMagicValid() const { + return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0); +} + +bool VdexFile::Header::IsVersionValid() const { + return (memcmp(version_, kVdexVersion, sizeof(kVdexVersion)) == 0); +} + +VdexFile::Header::Header() { + memcpy(magic_, kVdexMagic, sizeof(kVdexMagic)); + memcpy(version_, kVdexVersion, sizeof(kVdexVersion)); + DCHECK(IsMagicValid()); + DCHECK(IsVersionValid()); +} + +VdexFile* VdexFile::Open(const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg) { + if (!OS::FileExists(vdex_filename.c_str())) { + *error_msg = "File " + vdex_filename + " does not exist."; + return nullptr; + } + + std::unique_ptr vdex_file; + if (writable) { + vdex_file.reset(OS::OpenFileReadWrite(vdex_filename.c_str())); + } else { + vdex_file.reset(OS::OpenFileForReading(vdex_filename.c_str())); + } + if (vdex_file == nullptr) { + *error_msg = "Could not open file " + vdex_filename + + (writable ? " for read/write" : "for reading"); + return nullptr; + } + + int64_t vdex_length = vdex_file->GetLength(); + if (vdex_length == -1) { + *error_msg = "Could not read the length of file " + vdex_filename; + return nullptr; + } + + std::unique_ptr mmap(MemMap::MapFile(vdex_length, + writable ? PROT_READ | PROT_WRITE : PROT_READ, + MAP_SHARED, + vdex_file->Fd(), + 0 /* start offset */, + low_4gb, + vdex_filename.c_str(), + error_msg)); + if (mmap == nullptr) { + *error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg; + return nullptr; + } + + *error_msg = "Success"; + return new VdexFile(vdex_file.release(), mmap.release()); +} + +} // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h new file mode 100644 index 000000000..e381eb79c --- /dev/null +++ b/runtime/vdex_file.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_VDEX_FILE_H_ +#define ART_RUNTIME_VDEX_FILE_H_ + +#include +#include + +#include "base/macros.h" +#include "base/unix_file/fd_file.h" +#include "mem_map.h" +#include "os.h" + +namespace art { + +// VDEX files contain extracted DEX files. The VdexFile class maps the file to +// memory and provides tools for accessing its individual sections. +// +// File format: +// VdexFile::Header fixed-length header +// +// DEX[0] array of the input DEX files +// DEX[1] the bytecode may have been quickened +// ... +// DEX[D] +// + +class VdexFile { + public: + struct Header { + public: + Header(); + + bool IsMagicValid() const; + bool IsVersionValid() const; + + private: + static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; + static constexpr uint8_t kVdexVersion[] = { '0', '0', '0', '\0' }; + + uint8_t magic_[4]; + uint8_t version_[4]; + }; + + static VdexFile* Open(const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg); + + const uint8_t* Begin() const { return mmap_->Begin(); } + const uint8_t* End() const { return mmap_->End(); } + size_t Size() const { return mmap_->Size(); } + + private: + VdexFile(File* file, MemMap* mmap) : file_(file), mmap_(mmap) {} + + std::unique_ptr file_; + std::unique_ptr mmap_; + + DISALLOW_COPY_AND_ASSIGN(VdexFile); +}; + +} // namespace art + +#endif // ART_RUNTIME_VDEX_FILE_H_