Merge "Introduce VDEX file, use it for DEX files"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 2e1434b..7edc1cc 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -243,6 +243,11 @@
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 9cab3b9..da4609d 100644
--- a/build/art.go
+++ b/build/art.go
@@ -43,6 +43,10 @@
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 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 e1ee0d2..a18935f 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -73,10 +73,12 @@
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<const DexFile*, size_t> dex_file_to_oat_index_map;
std::vector<const char*> oat_filename_vector(1, oat_filename.c_str());
@@ -109,7 +111,7 @@
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<const uint8_t> raw_dex_file(
reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
@@ -120,16 +122,18 @@
}
std::unique_ptr<MemMap> opened_dex_files_map;
std::vector<std::unique_ptr<const DexFile>> 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 @@
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 @@
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 b1e3811..78e9ca9 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -125,7 +125,8 @@
/* profile_compilation_info */ nullptr));
}
- bool WriteElf(File* file,
+ bool WriteElf(File* vdex_file,
+ File* oat_file,
const std::vector<const DexFile*>& dex_files,
SafeMap<std::string, std::string>& key_value_store,
bool verify) {
@@ -141,10 +142,11 @@
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<const char*>& dex_filenames,
SafeMap<std::string, std::string>& key_value_store,
bool verify) {
@@ -155,10 +157,11 @@
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<std::string, std::string>& key_value_store,
@@ -168,10 +171,11 @@
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<std::string, std::string>& key_value_store,
bool verify) {
@@ -179,13 +183,13 @@
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<MemMap> opened_dex_files_map;
std::vector<std::unique_ptr<const DexFile>> 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 @@
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 @@
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
}
- ScratchFile tmp;
+ ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
SafeMap<std::string, std::string> 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<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
- tmp.GetFilename(),
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+ tmp_oat.GetFilename(),
nullptr,
nullptr,
false,
@@ -498,14 +506,14 @@
compiler_driver_->SetDexFilesForOatFile(dex_files);
compiler_driver_->CompileAll(class_loader, dex_files, &timings);
- ScratchFile tmp;
+ ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
SafeMap<std::string, std::string> 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<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
- tmp.GetFilename(),
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+ tmp_oat.GetFilename(),
nullptr,
nullptr,
false,
@@ -513,7 +521,8 @@
nullptr,
&error_msg));
ASSERT_TRUE(oat_file != nullptr);
- EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength()));
+ EXPECT_LT(static_cast<size_t>(oat_file->Size()),
+ static_cast<size_t>(tmp_oat.GetFile()->GetLength()));
}
static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>& data) {
@@ -559,10 +568,14 @@
ASSERT_TRUE(success);
input_filenames.push_back(dex_file2.GetFilename().c_str());
- ScratchFile oat_file;
+ ScratchFile oat_file, vdex_file(oat_file, ".vdex");
SafeMap<std::string, std::string> 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 @@
// Test using the AddDexFileSource() interface with the zip file.
std::vector<const char*> 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 @@
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 c9c5d24..43e01d5 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 @@
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 @@
}
bool OatWriter::WriteAndOpenDexFiles(
- OutputStream* rodata,
- File* file,
+ File* vdex_file,
+ OutputStream* oat_rodata,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
@@ -431,37 +437,67 @@
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
CHECK(write_state_ == WriteState::kAddingDexFileSources);
- size_t offset = InitOatHeader(instruction_set,
- instruction_set_features,
- dchecked_integral_cast<uint32_t>(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<MemMap> dex_files_map;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!WriteDexFiles(rodata, file) ||
- !OpenDexFiles(file, verify, &dex_files_map, &dex_files)) {
- return false;
+
+ // 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<uint32_t>(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<BufferedOutputStream> vdex_out(
+ MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(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;
+ }
+
+ // 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);
+ }
}
- // 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);
- }
-
- ChecksumUpdatingOutputStream checksum_updating_rodata(rodata, oat_header_.get());
-
+ // 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 @@
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 @@
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 @@
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 @@
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<size_t>(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<size_t>(oat_end_file_offset));
}
- CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset));
- CHECK_EQ(size_, relative_offset);
+ CHECK_EQ(file_offset + oat_size_, static_cast<size_t>(oat_end_file_offset));
+ CHECK_EQ(oat_size_, relative_offset);
write_state_ = WriteState::kWriteHeader;
return true;
@@ -1831,17 +1869,14 @@
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 @@
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<off_t>(start_offset)) {
+ off_t actual_offset = out->Seek(file_offset, kSeekSet);
+ if (actual_offset != static_cast<off_t>(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 @@
return false;
}
actual_offset = lseek(file->Fd(), 0, SEEK_CUR);
- if (actual_offset != static_cast<off_t>(start_offset)) {
+ if (actual_offset != static_cast<off_t>(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<off_t>(start_offset), rodata->Seek(0, kSeekCurrent));
+ size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_;
+ DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent));
// Extract the dex file and get the extracted size.
std::string error_msg;
@@ -1984,13 +2028,13 @@
<< " 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<off_t>(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 @@
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 @@
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<off_t>(start_offset), rodata->Seek(0, kSeekCurrent));
+ size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_;
+ DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent));
off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET);
if (input_offset != static_cast<off_t>(0)) {
@@ -2047,13 +2092,13 @@
<< " 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<off_t>(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 @@
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 @@
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 @@
}
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<MemMap> 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<MemMap> 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::WriteTypeLookupTables(
- OutputStream* rodata,
+ OutputStream* oat_rodata,
const std::vector<std::unique_ptr<const DexFile>>& 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<uint32_t>(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 @@
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<uint8_t> 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<size_t>(rodata->Seek(0u, kSeekCurrent)));
+ static_cast<size_t>(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 @@
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 93e2e44..77525f1 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -57,11 +57,6 @@
// ...
// 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 @@
CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
dchecked_vector<const char*> 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<std::string, std::string>* key_value_store,
@@ -183,8 +179,8 @@
return *oat_header_;
}
- size_t GetSize() const {
- return size_;
+ size_t GetOatSize() const {
+ return oat_size_;
}
size_t GetBssSize() const {
@@ -236,6 +232,25 @@
// 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<MemMap>* opened_dex_files_map,
+ /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
+
size_t InitOatHeader(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
uint32_t num_dex_files,
@@ -253,20 +268,10 @@
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<MemMap>* opened_dex_files_map,
- /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
- bool WriteTypeLookupTables(OutputStream* rodata,
+ bool WriteOatDexFiles(OutputStream* oat_rodata);
+ bool WriteTypeLookupTables(OutputStream* oat_rodata,
const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
void SetMultiOatRelativePatcherAdjustment();
@@ -300,8 +305,14 @@
// note OatFile does not take ownership of the DexFiles
const std::vector<const DexFile*>* 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 @@
std::unique_ptr<const std::vector<uint8_t>> 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 febfb63..1dd9132 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -507,6 +507,7 @@
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 @@
for (std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
map.release();
}
+ for (std::unique_ptr<File>& vdex_file : vdex_files_) {
+ vdex_file.release();
+ }
for (std::unique_ptr<File>& oat_file : oat_files_) {
oat_file.release();
}
@@ -578,6 +582,10 @@
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 @@
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 @@
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 @@
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 @@
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<File> 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<File> 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<File> oat_file(new File(oat_fd_, oat_location_, true));
- oat_file->DisableAutoClose();
- if (oat_file->SetLength(0) != 0) {
- PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
- }
+ std::unique_ptr<File> 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;
}
- if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
- PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
- oat_file->Erase();
+ oat_file->DisableAutoClose();
+ if (oat_file->SetLength(0) != 0) {
+ PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
+ }
+ oat_files_.push_back(std::move(oat_file));
+
+ DCHECK_NE(vdex_fd_, -1);
+ std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
+ std::unique_ptr<File> 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;
}
+ 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 @@
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 @@
// Unzip or copy dex files straight to the oat file.
std::unique_ptr<MemMap> opened_dex_files_map;
std::vector<std::unique_ptr<const DexFile>> 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 @@
// 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 @@
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 @@
image_writer_->UpdateOatFileLayout(i,
elf_writer->GetLoadedSize(),
oat_writer->GetOatDataOffset(),
- oat_writer->GetSize());
+ oat_writer->GetOatSize());
}
}
@@ -1774,12 +1822,8 @@
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 @@
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,13 +1884,32 @@
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();
+ bool FlushOutputFile(std::unique_ptr<File>* 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 FlushCloseOutputFile(std::unique_ptr<File>* file) {
+ if (file->get() != nullptr) {
+ std::unique_ptr<File> tmp(file->release());
+ if (tmp->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close output file: " << tmp->GetPath();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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;
}
}
@@ -1854,21 +1917,12 @@
return true;
}
- bool FlushCloseOatFile(size_t i) {
- if (oat_files_[i].get() != nullptr) {
- std::unique_ptr<File> tmp(oat_files_[i].release());
- if (tmp->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close oat file: " << oat_filenames_[i];
- return false;
- }
- }
- return true;
- }
-
- bool FlushCloseOatFiles() {
+ 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 @@
uint64_t start_ns_;
std::unique_ptr<WatchDog> watchdog_;
std::vector<std::unique_ptr<File>> oat_files_;
+ std::vector<std::unique_ptr<File>> vdex_files_;
std::string oat_location_;
std::vector<const char*> oat_filenames_;
std::vector<const char*> oat_unstripped_;
int oat_fd_;
+ int vdex_fd_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
int zip_fd_;
@@ -2603,8 +2659,8 @@
dex2oat.LoadClassProfileDescriptors();
dex2oat.Compile();
- if (!dex2oat.WriteOatFiles()) {
- dex2oat.EraseOatFiles();
+ if (!dex2oat.WriteOutputFiles()) {
+ dex2oat.EraseOutputFiles();
return EXIT_FAILURE;
}
@@ -2612,10 +2668,11 @@
// 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 @@
}
// 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 CompileApp(Dex2Oat& dex2oat) {
dex2oat.Compile();
- if (!dex2oat.WriteOatFiles()) {
- dex2oat.EraseOatFiles();
+ if (!dex2oat.WriteOutputFiles()) {
+ dex2oat.EraseOutputFiles();
return EXIT_FAILURE;
}
@@ -2657,7 +2714,7 @@
// When given --host, finish early without stripping.
if (dex2oat.IsHost()) {
- if (!dex2oat.FlushCloseOatFiles()) {
+ if (!dex2oat.FlushCloseOutputFiles()) {
return EXIT_FAILURE;
}
@@ -2672,7 +2729,7 @@
}
// Flush and close the files.
- if (!dex2oat.FlushCloseOatFiles()) {
+ if (!dex2oat.FlushCloseOutputFiles()) {
return EXIT_FAILURE;
}
@@ -2721,7 +2778,7 @@
}
if (!dex2oat->Setup()) {
- dex2oat->EraseOatFiles();
+ dex2oat->EraseOutputFiles();
return EXIT_FAILURE;
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index c87a18b..db6a709 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -719,10 +719,12 @@
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<uint32_t>(dex_file_pointer - oat_file_begin);
+ uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - vdex_file_begin);
os << StringPrintf("dex-file: 0x%08x..0x%08x\n",
dex_offset,
dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index b7ce02c..5240011 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 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 @@
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<File> input_oat_file(OS::OpenFileForReading(input_oat_filename.c_str()));
@@ -261,13 +288,16 @@
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 @@
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 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<File>
output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
if (output_oat_file.get() == nullptr) {
@@ -339,6 +376,9 @@
}
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 @@
UsageError(" --input-oat-fd=<file-descriptor>: Specifies the file-descriptor of the oat file");
UsageError(" to be patched.");
UsageError("");
+ UsageError(" --input-vdex-fd=<file-descriptor>: Specifies the file-descriptor of the vdex file");
+ UsageError(" associated with the oat file.");
+ UsageError("");
UsageError(" --input-oat-location=<file.oat>: 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 @@
UsageError(" file to.");
UsageError("");
UsageError(" --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the");
- UsageError(" the patched oat file to.");
+ UsageError(" patched oat file to.");
+ UsageError("");
+ UsageError(" --output-vdex-fd=<file-descriptor>: Specifies the file-descriptor to copy the");
+ UsageError(" the vdex file associated with the patch oat file to.");
UsageError("");
UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched");
UsageError(" image file to.");
@@ -1029,10 +1075,12 @@
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 @@
}
}
+ 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 @@
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<File> input_oat;
std::unique_ptr<File> output_oat;
@@ -1162,13 +1232,52 @@
}
}
+ // Open VDEX files if we are not symlinking them.
+ std::unique_ptr<File> input_vdex;
+ std::unique_ptr<File> 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 59e4a15..a884505 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -190,6 +190,7 @@
"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 aba5661..691bf55 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -166,6 +166,12 @@
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 3a4fa79..da9976a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -168,13 +168,11 @@
}
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 @@
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 5752fd9..cbc5d3c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -83,7 +83,8 @@
virtual ~OatFileBase() {}
template <typename kOatFileBaseSubType>
- 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 @@
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 @@
};
template <typename kOatFileBaseSubType>
-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 @@
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 @@
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 @@
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 @@
return false;
}
const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(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 @@
dex_file_location.c_str(),
dex_file_offset,
header->file_size_,
- Size());
+ DexSize());
return false;
}
@@ -942,31 +967,37 @@
: 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<DlOpenOatFile>(filename,
- location,
+ OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(vdex_filename,
+ oat_filename,
+ oat_location,
requested_base,
oat_file_begin,
- false,
+ false /* writable */,
executable,
low_4gb,
abs_dex_location,
@@ -975,7 +1006,7 @@
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 @@
//
// 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<ElfOatFile>(filename,
- location,
+ OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(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(const std::string& location, bool is_executable)
: location_(location),
+ vdex_(nullptr),
begin_(nullptr),
end_(nullptr),
bss_begin_(nullptr),
@@ -1071,6 +1104,14 @@
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 f5ab9dc..96e651e 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 @@
} // 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 @@
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 @@
// 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<VdexFile> vdex_;
+
// Pointer to OatHeader.
const uint8_t* begin_;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index fe6332d..415f991 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -345,17 +345,6 @@
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 @@
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 @@
return kUpdateNotAttempted;
}
- std::unique_ptr<File> oat_file;
- oat_file.reset(OS::CreateEmptyFile(oat_file_name.c_str()));
+ std::unique_ptr<File> 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<File> 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 @@
std::vector<std::string> 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 @@
std::unique_ptr<gc::space::ImageSpace> 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 313190c..d48edcf 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1213,6 +1213,15 @@
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 958f0a3..f3284e8 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -279,6 +279,13 @@
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 <typename A>
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
new file mode 100644
index 0000000..12bc451
--- /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 <memory>
+
+#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<File> 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<MemMap> 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 0000000..e381eb7
--- /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 <stdint.h>
+#include <string>
+
+#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> file_;
+ std::unique_ptr<MemMap> mmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(VdexFile);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_VDEX_FILE_H_