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/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