Refactor linker files from compiler/ to dex2oat/.
This shifts some code from the libart-compiler.so to dex2oat
and reduces memory needed for JIT. We also avoid loading the
libart-dexlayout.so for JIT but the memory savings are
minimal (one shared clean page, two shared dirty pages and
some per-app kernel mmap data) as the code has never been
needed in memory by JIT.
aosp_angler-userdebug file sizes (stripped):
lib64/libart-compiler.so: 2989112 -> 2671888 (-310KiB)
lib/libart-compiler.so: 2160816 -> 1939276 (-216KiB)
bin/dex2oat: 141868 -> 368808 (+222KiB)
LOAD/executable elf mapping sizes:
lib64/libart-compiler.so: 2866308 -> 2555500 (-304KiB)
lib/libart-compiler.so: 2050960 -> 1834836 (-211KiB)
bin/dex2oat: 129316 -> 345916 (+212KiB)
Test: m test-art-host-gtest
Test: testrunner.py --host
Test: cd art/; mma; cd -
Change-Id: If62f02847a6cbb208eaf7e1f3e91af4663fa4a5f
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index f13f01c..bdb8ff1 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -14,6 +14,78 @@
// limitations under the License.
//
+art_cc_defaults {
+ name: "libart-dex2oat-defaults",
+ defaults: ["art_defaults"],
+ host_supported: true,
+ clang: true,
+ srcs: [
+ "linker/elf_writer.cc",
+ "linker/elf_writer_quick.cc",
+ "linker/image_writer.cc",
+ "linker/multi_oat_relative_patcher.cc",
+ "linker/oat_writer.cc",
+ ],
+ target: {
+ host: {
+ // For compiler driver TLS.
+ host_ldlibs: ["-lpthread"],
+ },
+ android: {
+ // For atrace.
+ shared_libs: ["libcutils"],
+ },
+ },
+ generated_sources: ["art_dex2oat_operator_srcs"],
+ shared_libs: [
+ "libart-compiler",
+ "libart-dexlayout",
+ "libbase",
+ "liblz4",
+ "liblzma",
+ ],
+ export_include_dirs: ["."],
+
+ // For SHA-1 checksumming of build ID
+ static: {
+ whole_static_libs: ["libcrypto"],
+ },
+ shared: {
+ shared_libs: ["libcrypto"],
+ },
+}
+
+gensrcs {
+ name: "art_dex2oat_operator_srcs",
+ cmd: "$(location generate-operator-out.py) art/dex2oat $(in) > $(out)",
+ tool_files: ["generate-operator-out.py"],
+ srcs: [
+ "linker/image_writer.h",
+ ],
+ output_extension: "operator_out.cc",
+}
+
+art_cc_static_library {
+ name: "libart-dex2oat",
+ defaults: ["libart-dex2oat-defaults"],
+ shared_libs: [
+ "libart-compiler",
+ "libart"
+ ],
+}
+
+art_cc_static_library {
+ name: "libartd-dex2oat",
+ defaults: [
+ "art_debug_defaults",
+ "libart-dex2oat-defaults",
+ ],
+ shared_libs: [
+ "libartd-compiler",
+ "libartd"
+ ],
+}
+
cc_library_headers {
name: "dex2oat_headers",
host_supported: true,
@@ -45,11 +117,16 @@
"dex2oat-defaults",
],
shared_libs: [
- "libart",
"libart-compiler",
+ "libart-dexlayout",
+ "libart",
"libbase",
+ "liblz4",
"libsigchain",
],
+ static_libs: [
+ "libart-dex2oat",
+ ]
}
art_cc_binary {
@@ -59,11 +136,16 @@
"dex2oat-defaults",
],
shared_libs: [
- "libartd",
"libartd-compiler",
+ "libartd-dexlayout",
+ "libartd",
"libbase",
+ "liblz4",
"libsigchain",
],
+ static_libs: [
+ "libartd-dex2oat",
+ ]
}
art_cc_binary {
@@ -85,6 +167,7 @@
"-z muldefs",
],
static_libs: [
+ "libart-dex2oat",
"libart-compiler",
"libart-dexlayout",
"libart",
@@ -115,6 +198,7 @@
"-z muldefs",
],
static_libs: [
+ "libartd-dex2oat",
"libartd-compiler",
"libartd-dexlayout",
"libartd",
@@ -131,6 +215,21 @@
srcs: [
"dex2oat_test.cc",
"dex2oat_image_test.cc",
+ "linker/elf_writer_test.cc",
+ "linker/image_test.cc",
+ "linker/image_write_read_test.cc",
+ "linker/multi_oat_relative_patcher_test.cc",
+ "linker/oat_writer_test.cc",
],
header_libs: ["dex2oat_headers"],
+ shared_libs: [
+ "libartd-compiler",
+ "libartd-dexlayout",
+ "libbase",
+ "liblz4",
+ "libsigchain",
+ ],
+ static_libs: [
+ "libartd-dex2oat",
+ ]
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index d8caf42..21d3895 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -61,19 +61,20 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "elf_file.h"
-#include "elf_writer.h"
-#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
#include "gc/verification.h"
-#include "image_writer.h"
#include "interpreter/unstarted_runtime.h"
#include "java_vm_ext.h"
#include "jit/profile_compilation_info.h"
#include "leb128.h"
#include "linker/buffered_output_stream.h"
+#include "linker/elf_writer.h"
+#include "linker/elf_writer_quick.h"
#include "linker/file_output_stream.h"
+#include "linker/image_writer.h"
#include "linker/multi_oat_relative_patcher.h"
+#include "linker/oat_writer.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
@@ -81,7 +82,6 @@
#include "nativehelper/ScopedLocalRef.h"
#include "oat_file.h"
#include "oat_file_assistant.h"
-#include "oat_writer.h"
#include "os.h"
#include "runtime.h"
#include "runtime_options.h"
@@ -1455,8 +1455,9 @@
// Note: we're only invalidating the magic data in the file, as dex2oat needs the rest of
// the information to remain valid.
if (update_input_vdex_) {
- std::unique_ptr<BufferedOutputStream> vdex_out = std::make_unique<BufferedOutputStream>(
- std::make_unique<FileOutputStream>(vdex_files_.back().get()));
+ std::unique_ptr<linker::BufferedOutputStream> vdex_out =
+ std::make_unique<linker::BufferedOutputStream>(
+ std::make_unique<linker::FileOutputStream>(vdex_files_.back().get()));
if (!vdex_out->WriteFully(&VdexFile::Header::kVdexInvalidMagic,
arraysize(VdexFile::Header::kVdexInvalidMagic))) {
PLOG(ERROR) << "Failed to invalidate vdex header. File: " << vdex_out->GetLocation();
@@ -2026,14 +2027,14 @@
VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(image_base_);
}
- image_writer_.reset(new ImageWriter(*driver_,
- image_base_,
- compiler_options_->GetCompilePic(),
- IsAppImage(),
- image_storage_mode_,
- oat_filenames_,
- dex_file_oat_index_map_,
- dirty_image_objects_.get()));
+ image_writer_.reset(new linker::ImageWriter(*driver_,
+ image_base_,
+ compiler_options_->GetCompilePic(),
+ IsAppImage(),
+ image_storage_mode_,
+ oat_filenames_,
+ dex_file_oat_index_map_,
+ dirty_image_objects_.get()));
// We need to prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -2046,7 +2047,7 @@
// Initialize the writers with the compiler driver, image writer, and their
// dex files. The writers were created without those being there yet.
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
- std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
+ std::unique_ptr<linker::OatWriter>& oat_writer = oat_writers_[i];
std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i];
oat_writer->Initialize(driver_.get(), image_writer_.get(), dex_files);
}
@@ -2057,8 +2058,9 @@
verifier::VerifierDeps* verifier_deps = callbacks_->GetVerifierDeps();
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
File* vdex_file = vdex_files_[i].get();
- std::unique_ptr<BufferedOutputStream> vdex_out =
- std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
+ std::unique_ptr<linker::BufferedOutputStream> vdex_out =
+ std::make_unique<linker::BufferedOutputStream>(
+ std::make_unique<linker::FileOutputStream>(vdex_file));
if (!oat_writers_[i]->WriteVerifierDeps(vdex_out.get(), verifier_deps)) {
LOG(ERROR) << "Failed to write verifier dependencies into VDEX " << vdex_file->GetPath();
@@ -2082,8 +2084,8 @@
TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_);
linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get());
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
- std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i];
- std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
+ std::unique_ptr<linker::ElfWriter>& elf_writer = elf_writers_[i];
+ std::unique_ptr<linker::OatWriter>& oat_writer = oat_writers_[i];
oat_writer->PrepareLayout(&patcher);
@@ -2116,14 +2118,14 @@
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
std::unique_ptr<File>& oat_file = oat_files_[i];
- std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i];
- std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
+ std::unique_ptr<linker::ElfWriter>& elf_writer = elf_writers_[i];
+ std::unique_ptr<linker::OatWriter>& oat_writer = oat_writers_[i];
// We need to mirror the layout of the ELF file in the compressed debug-info.
// Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above.
elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo());
- OutputStream*& rodata = rodata_[i];
+ linker::OutputStream*& rodata = rodata_[i];
DCHECK(rodata != nullptr);
if (!oat_writer->WriteRodata(rodata)) {
LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file->GetPath();
@@ -2132,7 +2134,7 @@
elf_writer->EndRoData(rodata);
rodata = nullptr;
- OutputStream* text = elf_writer->StartText();
+ linker::OutputStream* text = elf_writer->StartText();
if (!oat_writer->WriteCode(text)) {
LOG(ERROR) << "Failed to write .text section to the ELF file " << oat_file->GetPath();
return false;
@@ -2510,13 +2512,13 @@
elf_writers_.reserve(oat_files_.size());
oat_writers_.reserve(oat_files_.size());
for (const std::unique_ptr<File>& oat_file : oat_files_) {
- elf_writers_.emplace_back(CreateElfWriterQuick(instruction_set_,
- instruction_set_features_.get(),
- compiler_options_.get(),
- oat_file.get()));
+ elf_writers_.emplace_back(linker::CreateElfWriterQuick(instruction_set_,
+ instruction_set_features_.get(),
+ compiler_options_.get(),
+ oat_file.get()));
elf_writers_.back()->Start();
const bool do_dexlayout = DoDexLayoutOptimizations();
- oat_writers_.emplace_back(new OatWriter(
+ oat_writers_.emplace_back(new linker::OatWriter(
IsBootImage(), timings_, do_dexlayout ? profile_compilation_info_.get() : nullptr));
}
}
@@ -2679,7 +2681,7 @@
return false;
}
- if (!ElfWriter::Fixup(oat_file.get(), oat_data_begins[i])) {
+ if (!linker::ElfWriter::Fixup(oat_file.get(), oat_data_begins[i])) {
oat_file->Erase();
LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
return false;
@@ -2882,11 +2884,11 @@
std::vector<const DexFile*> dex_files_;
std::string no_inline_from_string_;
- std::vector<std::unique_ptr<ElfWriter>> elf_writers_;
- std::vector<std::unique_ptr<OatWriter>> oat_writers_;
- std::vector<OutputStream*> rodata_;
- std::vector<std::unique_ptr<OutputStream>> vdex_out_;
- std::unique_ptr<ImageWriter> image_writer_;
+ std::vector<std::unique_ptr<linker::ElfWriter>> elf_writers_;
+ std::vector<std::unique_ptr<linker::OatWriter>> oat_writers_;
+ std::vector<linker::OutputStream*> rodata_;
+ std::vector<std::unique_ptr<linker::OutputStream>> vdex_out_;
+ std::unique_ptr<linker::ImageWriter> image_writer_;
std::unique_ptr<CompilerDriver> driver_;
std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
diff --git a/dex2oat/generate-operator-out.py b/dex2oat/generate-operator-out.py
new file mode 120000
index 0000000..cc291d2
--- /dev/null
+++ b/dex2oat/generate-operator-out.py
@@ -0,0 +1 @@
+../tools/generate-operator-out.py
\ No newline at end of file
diff --git a/dex2oat/linker/elf_writer.cc b/dex2oat/linker/elf_writer.cc
new file mode 100644
index 0000000..ca34864
--- /dev/null
+++ b/dex2oat/linker/elf_writer.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 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 "elf_writer.h"
+
+#include "base/unix_file/fd_file.h"
+#include "elf_file.h"
+
+namespace art {
+namespace linker {
+
+uintptr_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) {
+ uintptr_t oatdata_address = elf_file->FindSymbolAddress(SHT_DYNSYM,
+ "oatdata",
+ false);
+ CHECK_NE(0U, oatdata_address);
+ return oatdata_address;
+}
+
+void ElfWriter::GetOatElfInformation(File* file,
+ size_t* oat_loaded_size,
+ size_t* oat_data_offset) {
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file,
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
+ CHECK(elf_file.get() != nullptr) << error_msg;
+
+ bool success = elf_file->GetLoadedSize(oat_loaded_size, &error_msg);
+ CHECK(success) << error_msg;
+ CHECK_NE(0U, *oat_loaded_size);
+ *oat_data_offset = GetOatDataAddress(elf_file.get());
+ CHECK_NE(0U, *oat_data_offset);
+}
+
+bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, &error_msg));
+ CHECK(elf_file.get() != nullptr) << error_msg;
+
+ // Lookup "oatdata" symbol address.
+ uintptr_t oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
+ uintptr_t base_address = oat_data_begin - oatdata_address;
+
+ return elf_file->Fixup(base_address);
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h
new file mode 100644
index 0000000..0eb36ed
--- /dev/null
+++ b/dex2oat/linker/elf_writer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 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_DEX2OAT_LINKER_ELF_WRITER_H_
+#define ART_DEX2OAT_LINKER_ELF_WRITER_H_
+
+#include <stdint.h>
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "base/array_ref.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "os.h"
+
+namespace art {
+
+class ElfFile;
+
+namespace debug {
+struct MethodDebugInfo;
+} // namespace debug
+
+namespace linker {
+
+class OutputStream;
+
+class ElfWriter {
+ public:
+ // Looks up information about location of oat file in elf file container.
+ // Used for ImageWriter to perform memory layout.
+ static void GetOatElfInformation(File* file,
+ size_t* oat_loaded_size,
+ size_t* oat_data_offset);
+
+ // Returns runtime oat_data runtime address for an opened ElfFile.
+ static uintptr_t GetOatDataAddress(ElfFile* elf_file);
+
+ static bool Fixup(File* file, uintptr_t oat_data_begin);
+
+ virtual ~ElfWriter() {}
+
+ virtual void Start() = 0;
+ virtual void PrepareDynamicSection(size_t rodata_size,
+ size_t text_size,
+ size_t bss_size,
+ size_t bss_methods_offset,
+ size_t bss_roots_offset) = 0;
+ virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
+ virtual OutputStream* StartRoData() = 0;
+ virtual void EndRoData(OutputStream* rodata) = 0;
+ virtual OutputStream* StartText() = 0;
+ virtual void EndText(OutputStream* text) = 0;
+ virtual void WriteDynamicSection() = 0;
+ virtual void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
+ virtual bool End() = 0;
+
+ // Get the ELF writer's stream. This stream can be used for writing data directly
+ // to a section after the section has been finished. When that's done, the user
+ // should Seek() back to the position where the stream was before this operation.
+ virtual OutputStream* GetStream() = 0;
+
+ // Get the size that the loaded ELF file will occupy in memory.
+ virtual size_t GetLoadedSize() = 0;
+
+ protected:
+ ElfWriter() = default;
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_DEX2OAT_LINKER_ELF_WRITER_H_
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
new file mode 100644
index 0000000..93f5a1d
--- /dev/null
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2012 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 "elf_writer_quick.h"
+
+#include <openssl/sha.h>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "base/casts.h"
+#include "base/logging.h"
+#include "compiled_method.h"
+#include "debug/elf_debug_writer.h"
+#include "debug/method_debug_info.h"
+#include "driver/compiler_options.h"
+#include "elf.h"
+#include "elf_utils.h"
+#include "globals.h"
+#include "leb128.h"
+#include "linker/buffered_output_stream.h"
+#include "linker/elf_builder.h"
+#include "linker/file_output_stream.h"
+#include "thread-current-inl.h"
+#include "thread_pool.h"
+#include "utils.h"
+
+namespace art {
+namespace linker {
+
+// .eh_frame and .debug_frame are almost identical.
+// Except for some minor formatting differences, the main difference
+// is that .eh_frame is allocated within the running program because
+// it is used by C++ exception handling (which we do not use so we
+// can choose either). C++ compilers generally tend to use .eh_frame
+// because if they need it sometimes, they might as well always use it.
+// Let's use .debug_frame because it is easier to strip or compress.
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
+
+class DebugInfoTask : public Task {
+ public:
+ DebugInfoTask(InstructionSet isa,
+ const InstructionSetFeatures* features,
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos)
+ : isa_(isa),
+ instruction_set_features_(features),
+ rodata_section_size_(rodata_section_size),
+ text_section_size_(text_section_size),
+ method_infos_(method_infos) {
+ }
+
+ void Run(Thread*) {
+ result_ = debug::MakeMiniDebugInfo(isa_,
+ instruction_set_features_,
+ rodata_section_size_,
+ text_section_size_,
+ method_infos_);
+ }
+
+ std::vector<uint8_t>* GetResult() {
+ return &result_;
+ }
+
+ private:
+ InstructionSet isa_;
+ const InstructionSetFeatures* instruction_set_features_;
+ size_t rodata_section_size_;
+ size_t text_section_size_;
+ const ArrayRef<const debug::MethodDebugInfo> method_infos_;
+ std::vector<uint8_t> result_;
+};
+
+template <typename ElfTypes>
+class ElfWriterQuick FINAL : public ElfWriter {
+ public:
+ ElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
+ const CompilerOptions* compiler_options,
+ File* elf_file);
+ ~ElfWriterQuick();
+
+ void Start() OVERRIDE;
+ void PrepareDynamicSection(size_t rodata_size,
+ size_t text_size,
+ size_t bss_size,
+ size_t bss_methods_offset,
+ size_t bss_roots_offset) OVERRIDE;
+ void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
+ OutputStream* StartRoData() OVERRIDE;
+ void EndRoData(OutputStream* rodata) OVERRIDE;
+ OutputStream* StartText() OVERRIDE;
+ void EndText(OutputStream* text) OVERRIDE;
+ void WriteDynamicSection() OVERRIDE;
+ void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
+ bool End() OVERRIDE;
+
+ virtual OutputStream* GetStream() OVERRIDE;
+
+ size_t GetLoadedSize() OVERRIDE;
+
+ static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
+ std::vector<uint8_t>* buffer);
+
+ private:
+ const InstructionSetFeatures* instruction_set_features_;
+ const CompilerOptions* const compiler_options_;
+ File* const elf_file_;
+ size_t rodata_size_;
+ size_t text_size_;
+ size_t bss_size_;
+ std::unique_ptr<BufferedOutputStream> output_stream_;
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder_;
+ std::unique_ptr<DebugInfoTask> debug_info_task_;
+ std::unique_ptr<ThreadPool> debug_info_thread_pool_;
+
+ void ComputeFileBuildId(uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
+};
+
+std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
+ const CompilerOptions* compiler_options,
+ File* elf_file) {
+ if (Is64BitInstructionSet(instruction_set)) {
+ return std::make_unique<ElfWriterQuick<ElfTypes64>>(instruction_set,
+ features,
+ compiler_options,
+ elf_file);
+ } else {
+ return std::make_unique<ElfWriterQuick<ElfTypes32>>(instruction_set,
+ features,
+ compiler_options,
+ elf_file);
+ }
+}
+
+template <typename ElfTypes>
+ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
+ const CompilerOptions* compiler_options,
+ File* elf_file)
+ : ElfWriter(),
+ instruction_set_features_(features),
+ compiler_options_(compiler_options),
+ elf_file_(elf_file),
+ rodata_size_(0u),
+ text_size_(0u),
+ bss_size_(0u),
+ output_stream_(
+ std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(elf_file))),
+ builder_(new ElfBuilder<ElfTypes>(instruction_set, features, output_stream_.get())) {}
+
+template <typename ElfTypes>
+ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::Start() {
+ builder_->Start();
+ if (compiler_options_->GetGenerateBuildId()) {
+ builder_->WriteBuildIdSection();
+ }
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size,
+ size_t text_size,
+ size_t bss_size,
+ size_t bss_methods_offset,
+ size_t bss_roots_offset) {
+ DCHECK_EQ(rodata_size_, 0u);
+ rodata_size_ = rodata_size;
+ DCHECK_EQ(text_size_, 0u);
+ text_size_ = text_size;
+ DCHECK_EQ(bss_size_, 0u);
+ bss_size_ = bss_size;
+ builder_->PrepareDynamicSection(elf_file_->GetPath(),
+ rodata_size_,
+ text_size_,
+ bss_size_,
+ bss_methods_offset,
+ bss_roots_offset);
+}
+
+template <typename ElfTypes>
+OutputStream* ElfWriterQuick<ElfTypes>::StartRoData() {
+ auto* rodata = builder_->GetRoData();
+ rodata->Start();
+ return rodata;
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::EndRoData(OutputStream* rodata) {
+ CHECK_EQ(builder_->GetRoData(), rodata);
+ builder_->GetRoData()->End();
+}
+
+template <typename ElfTypes>
+OutputStream* ElfWriterQuick<ElfTypes>::StartText() {
+ auto* text = builder_->GetText();
+ text->Start();
+ return text;
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) {
+ CHECK_EQ(builder_->GetText(), text);
+ builder_->GetText()->End();
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::WriteDynamicSection() {
+ if (bss_size_ != 0u) {
+ builder_->GetBss()->WriteNoBitsSection(bss_size_);
+ }
+ if (builder_->GetIsa() == kMips || builder_->GetIsa() == kMips64) {
+ builder_->WriteMIPSabiflagsSection();
+ }
+ builder_->WriteDynamicSection();
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
+ if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) {
+ // Prepare the mini-debug-info in background while we do other I/O.
+ Thread* self = Thread::Current();
+ debug_info_task_ = std::unique_ptr<DebugInfoTask>(
+ new DebugInfoTask(builder_->GetIsa(),
+ instruction_set_features_,
+ rodata_size_,
+ text_size_,
+ method_infos));
+ debug_info_thread_pool_ = std::unique_ptr<ThreadPool>(
+ new ThreadPool("Mini-debug-info writer", 1));
+ debug_info_thread_pool_->AddTask(self, debug_info_task_.get());
+ debug_info_thread_pool_->StartWorkers(self);
+ }
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::WriteDebugInfo(
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
+ if (!method_infos.empty()) {
+ if (compiler_options_->GetGenerateDebugInfo()) {
+ // Generate all the debug information we can.
+ debug::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat, true /* write_oat_patches */);
+ }
+ if (compiler_options_->GetGenerateMiniDebugInfo()) {
+ // Wait for the mini-debug-info generation to finish and write it to disk.
+ Thread* self = Thread::Current();
+ DCHECK(debug_info_thread_pool_ != nullptr);
+ debug_info_thread_pool_->Wait(self, true, false);
+ builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
+ }
+ }
+}
+
+template <typename ElfTypes>
+bool ElfWriterQuick<ElfTypes>::End() {
+ builder_->End();
+ if (compiler_options_->GetGenerateBuildId()) {
+ uint8_t build_id[ElfBuilder<ElfTypes>::kBuildIdLen];
+ ComputeFileBuildId(&build_id);
+ builder_->WriteBuildId(build_id);
+ }
+ return builder_->Good();
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::ComputeFileBuildId(
+ uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]) {
+ constexpr int kBufSize = 8192;
+ std::vector<char> buffer(kBufSize);
+ int64_t offset = 0;
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ while (true) {
+ int64_t bytes_read = elf_file_->Read(buffer.data(), kBufSize, offset);
+ CHECK_GE(bytes_read, 0);
+ if (bytes_read == 0) {
+ // End of file.
+ break;
+ }
+ SHA1_Update(&ctx, buffer.data(), bytes_read);
+ offset += bytes_read;
+ }
+ SHA1_Final(*build_id, &ctx);
+}
+
+template <typename ElfTypes>
+OutputStream* ElfWriterQuick<ElfTypes>::GetStream() {
+ return builder_->GetStream();
+}
+
+template <typename ElfTypes>
+size_t ElfWriterQuick<ElfTypes>::GetLoadedSize() {
+ return builder_->GetLoadedSize();
+}
+
+// Explicit instantiations
+template class ElfWriterQuick<ElfTypes32>;
+template class ElfWriterQuick<ElfTypes64>;
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/elf_writer_quick.h b/dex2oat/linker/elf_writer_quick.h
new file mode 100644
index 0000000..e20957c
--- /dev/null
+++ b/dex2oat/linker/elf_writer_quick.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 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_DEX2OAT_LINKER_ELF_WRITER_QUICK_H_
+#define ART_DEX2OAT_LINKER_ELF_WRITER_QUICK_H_
+
+#include <memory>
+
+#include "arch/instruction_set.h"
+#include "elf_writer.h"
+#include "os.h"
+
+namespace art {
+
+class CompilerOptions;
+class InstructionSetFeatures;
+
+namespace linker {
+
+std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
+ const CompilerOptions* compiler_options,
+ File* elf_file);
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_DEX2OAT_LINKER_ELF_WRITER_QUICK_H_
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
new file mode 100644
index 0000000..9f8ed77
--- /dev/null
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2011 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 "elf_file.h"
+
+#include "base/unix_file/fd_file.h"
+#include "common_compiler_test.h"
+#include "elf_file.h"
+#include "elf_file_impl.h"
+#include "elf_writer_quick.h"
+#include "linker/elf_builder.h"
+#include "oat.h"
+#include "utils.h"
+
+namespace art {
+namespace linker {
+
+class ElfWriterTest : public CommonCompilerTest {
+ protected:
+ virtual void SetUp() {
+ ReserveImageSpace();
+ CommonCompilerTest::SetUp();
+ }
+};
+
+#define EXPECT_ELF_FILE_ADDRESS(ef, expected_value, symbol_name, build_map) \
+ do { \
+ void* addr = reinterpret_cast<void*>((ef)->FindSymbolAddress(SHT_DYNSYM, \
+ symbol_name, \
+ build_map)); \
+ EXPECT_NE(nullptr, addr); \
+ if ((expected_value) == nullptr) { \
+ (expected_value) = addr; \
+ } \
+ EXPECT_EQ(expected_value, addr); \
+ EXPECT_EQ(expected_value, (ef)->FindDynamicSymbolAddress(symbol_name)); \
+ } while (false)
+
+TEST_F(ElfWriterTest, dlsym) {
+ std::string elf_location = GetCoreOatLocation();
+ std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA);
+ LOG(INFO) << "elf_filename=" << elf_filename;
+
+ UnreserveImageSpace();
+ void* dl_oatdata = nullptr;
+ void* dl_oatexec = nullptr;
+ void* dl_oatlastword = nullptr;
+
+ std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
+ ASSERT_TRUE(file.get() != nullptr) << elf_filename;
+ {
+ std::string error_msg;
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false);
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false);
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", false);
+ }
+ {
+ std::string error_msg;
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true);
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true);
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true);
+ }
+ {
+ uint8_t* base = reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS);
+ std::string error_msg;
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ true,
+ /*low_4gb*/false,
+ &error_msg,
+ base));
+ CHECK(ef.get() != nullptr) << error_msg;
+ CHECK(ef->Load(file.get(), false, /*low_4gb*/false, &error_msg)) << error_msg;
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatdata) + reinterpret_cast<uintptr_t>(base),
+ reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatdata")));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatexec) + reinterpret_cast<uintptr_t>(base),
+ reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatexec")));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatlastword) + reinterpret_cast<uintptr_t>(base),
+ reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatlastword")));
+ }
+}
+
+TEST_F(ElfWriterTest, CheckBuildIdPresent) {
+ std::string elf_location = GetCoreOatLocation();
+ std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA);
+ LOG(INFO) << "elf_filename=" << elf_filename;
+
+ std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
+ ASSERT_TRUE(file.get() != nullptr);
+ {
+ std::string error_msg;
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
+ EXPECT_TRUE(ef->HasSection(".note.gnu.build-id"));
+ }
+}
+
+TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
+ const std::vector<std::vector<uintptr_t>> test_data {
+ { 0, 4, 8, 15, 128, 200 },
+ { 8, 8 + 127 },
+ { 8, 8 + 128 },
+ { },
+ };
+ for (const auto& patch_locations : test_data) {
+ constexpr int32_t delta = 0x11235813;
+
+ // Encode patch locations.
+ std::vector<uint8_t> oat_patches;
+ ElfBuilder<ElfTypes32>::EncodeOatPatches(ArrayRef<const uintptr_t>(patch_locations),
+ &oat_patches);
+
+ // Create buffer to be patched.
+ std::vector<uint8_t> initial_data(256);
+ for (size_t i = 0; i < initial_data.size(); i++) {
+ initial_data[i] = i;
+ }
+
+ // Patch manually.
+ std::vector<uint8_t> expected = initial_data;
+ for (uintptr_t location : patch_locations) {
+ typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress;
+ *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
+ }
+
+ // Decode and apply patch locations.
+ std::vector<uint8_t> actual = initial_data;
+ ElfFileImpl32::ApplyOatPatches(
+ oat_patches.data(), oat_patches.data() + oat_patches.size(), delta,
+ actual.data(), actual.data() + actual.size());
+
+ EXPECT_EQ(expected, actual);
+ }
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
new file mode 100644
index 0000000..ab6e7a8
--- /dev/null
+++ b/dex2oat/linker/image_test.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 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 <string.h>
+#include <vector>
+
+#include "image_test.h"
+
+#include "image.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+namespace art {
+namespace linker {
+
+TEST_F(ImageTest, TestImageLayout) {
+ std::vector<size_t> image_sizes;
+ std::vector<size_t> image_sizes_extra;
+ // Compile multi-image with ImageLayoutA being the last image.
+ {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", {"LMyClass;"});
+ image_sizes = helper.GetImageObjectSectionSizes();
+ }
+ TearDown();
+ runtime_.reset();
+ SetUp();
+ // Compile multi-image with ImageLayoutB being the last image.
+ {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", {"LMyClass;"});
+ image_sizes_extra = helper.GetImageObjectSectionSizes();
+ }
+ // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the
+ // first two images.
+ ASSERT_EQ(image_sizes.size(), image_sizes.size());
+ // Sizes of the object sections should be the same for all but the last image.
+ for (size_t i = 0; i < image_sizes.size() - 1; ++i) {
+ EXPECT_EQ(image_sizes[i], image_sizes_extra[i]);
+ }
+ // Last image should be larger since it has a hash map and a string.
+ EXPECT_LT(image_sizes.back(), image_sizes_extra.back());
+}
+
+TEST_F(ImageTest, ImageHeaderIsValid) {
+ uint32_t image_begin = ART_BASE_ADDRESS;
+ uint32_t image_size_ = 16 * KB;
+ uint32_t image_roots = ART_BASE_ADDRESS + (1 * KB);
+ uint32_t oat_checksum = 0;
+ uint32_t oat_file_begin = ART_BASE_ADDRESS + (4 * KB); // page aligned
+ uint32_t oat_data_begin = ART_BASE_ADDRESS + (8 * KB); // page aligned
+ uint32_t oat_data_end = ART_BASE_ADDRESS + (9 * KB);
+ uint32_t oat_file_end = ART_BASE_ADDRESS + (10 * KB);
+ ImageSection sections[ImageHeader::kSectionCount];
+ ImageHeader image_header(image_begin,
+ image_size_,
+ sections,
+ image_roots,
+ oat_checksum,
+ oat_file_begin,
+ oat_data_begin,
+ oat_data_end,
+ oat_file_end,
+ /*boot_image_begin*/0U,
+ /*boot_image_size*/0U,
+ /*boot_oat_begin*/0U,
+ /*boot_oat_size_*/0U,
+ sizeof(void*),
+ /*compile_pic*/false,
+ /*is_pic*/false,
+ ImageHeader::kDefaultStorageMode,
+ /*data_size*/0u);
+ ASSERT_TRUE(image_header.IsValid());
+ ASSERT_TRUE(!image_header.IsAppImage());
+
+ char* magic = const_cast<char*>(image_header.GetMagic());
+ strcpy(magic, ""); // bad magic
+ ASSERT_FALSE(image_header.IsValid());
+ strcpy(magic, "art\n000"); // bad version
+ ASSERT_FALSE(image_header.IsValid());
+}
+
+// Test that pointer to quick code is the same in
+// a default method of an interface and in a copied method
+// of a class which implements the interface. This should be true
+// only if the copied method and the origin method are located in the
+// same oat file.
+TEST_F(ImageTest, TestDefaultMethods) {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed,
+ helper,
+ "DefaultMethods",
+ {"LIface;", "LImpl;", "LIterableBase;"});
+
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ // Test the pointer to quick code is the same in origin method
+ // and in the copied method form the same oat file.
+ mirror::Class* iface_klass = class_linker_->LookupClass(
+ self, "LIface;", ObjPtr<mirror::ClassLoader>());
+ ASSERT_NE(nullptr, iface_klass);
+ ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size);
+ ASSERT_NE(nullptr, origin);
+ ASSERT_TRUE(origin->GetDeclaringClass() == iface_klass);
+ const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+ // The origin method should have a pointer to quick code
+ ASSERT_NE(nullptr, code);
+ ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code));
+ mirror::Class* impl_klass = class_linker_->LookupClass(
+ self, "LImpl;", ObjPtr<mirror::ClassLoader>());
+ ASSERT_NE(nullptr, impl_klass);
+ ArtMethod* copied = FindCopiedMethod(origin, impl_klass);
+ ASSERT_NE(nullptr, copied);
+ // the copied method should have pointer to the same quick code as the origin method
+ ASSERT_EQ(code, copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size));
+
+ // Test the origin method has pointer to quick code
+ // but the copied method has pointer to interpreter
+ // because these methods are in different oat files.
+ mirror::Class* iterable_klass = class_linker_->LookupClass(
+ self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>());
+ ASSERT_NE(nullptr, iterable_klass);
+ origin = iterable_klass->FindClassMethod(
+ "forEach", "(Ljava/util/function/Consumer;)V", pointer_size);
+ ASSERT_NE(nullptr, origin);
+ ASSERT_FALSE(origin->IsDirect());
+ ASSERT_TRUE(origin->GetDeclaringClass() == iterable_klass);
+ code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+ // the origin method should have a pointer to quick code
+ ASSERT_NE(nullptr, code);
+ ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code));
+ mirror::Class* iterablebase_klass = class_linker_->LookupClass(
+ self, "LIterableBase;", ObjPtr<mirror::ClassLoader>());
+ ASSERT_NE(nullptr, iterablebase_klass);
+ copied = FindCopiedMethod(origin, iterablebase_klass);
+ ASSERT_NE(nullptr, copied);
+ code = copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+ // the copied method should have a pointer to interpreter
+ ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code));
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
new file mode 100644
index 0000000..71f1fa6
--- /dev/null
+++ b/dex2oat/linker/image_test.h
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2011 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_DEX2OAT_LINKER_IMAGE_TEST_H_
+#define ART_DEX2OAT_LINKER_IMAGE_TEST_H_
+
+#include "image.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+
+#include "art_method-inl.h"
+#include "base/unix_file/fd_file.h"
+#include "class_linker-inl.h"
+#include "common_compiler_test.h"
+#include "compiler_callbacks.h"
+#include "debug/method_debug_info.h"
+#include "dex/quick_compiler_callbacks.h"
+#include "driver/compiler_options.h"
+#include "gc/space/image_space.h"
+#include "image_writer.h"
+#include "linker/buffered_output_stream.h"
+#include "linker/elf_writer.h"
+#include "linker/elf_writer_quick.h"
+#include "linker/file_output_stream.h"
+#include "linker/multi_oat_relative_patcher.h"
+#include "lock_word.h"
+#include "mirror/object-inl.h"
+#include "oat_writer.h"
+#include "scoped_thread_state_change-inl.h"
+#include "signal_catcher.h"
+#include "utils.h"
+
+namespace art {
+namespace linker {
+
+static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS;
+
+struct CompilationHelper {
+ std::vector<std::string> dex_file_locations;
+ std::vector<ScratchFile> image_locations;
+ std::vector<std::unique_ptr<const DexFile>> extra_dex_files;
+ std::vector<ScratchFile> image_files;
+ std::vector<ScratchFile> oat_files;
+ std::vector<ScratchFile> vdex_files;
+ std::string image_dir;
+
+ void Compile(CompilerDriver* driver,
+ ImageHeader::StorageMode storage_mode);
+
+ std::vector<size_t> GetImageObjectSectionSizes();
+
+ ~CompilationHelper();
+};
+
+class ImageTest : public CommonCompilerTest {
+ protected:
+ virtual void SetUp() {
+ ReserveImageSpace();
+ CommonCompilerTest::SetUp();
+ }
+
+ void TestWriteRead(ImageHeader::StorageMode storage_mode);
+
+ void Compile(ImageHeader::StorageMode storage_mode,
+ CompilationHelper& out_helper,
+ const std::string& extra_dex = "",
+ const std::initializer_list<std::string>& image_classes = {});
+
+ void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+ CommonCompilerTest::SetUpRuntimeOptions(options);
+ QuickCompilerCallbacks* new_callbacks =
+ new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileBootImage);
+ new_callbacks->SetVerificationResults(verification_results_.get());
+ callbacks_.reset(new_callbacks);
+ options->push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+ }
+
+ std::unordered_set<std::string>* GetImageClasses() OVERRIDE {
+ return new std::unordered_set<std::string>(image_classes_);
+ }
+
+ ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) {
+ if (strcmp(origin->GetName(), m.GetName()) == 0 &&
+ origin->GetSignature() == m.GetSignature()) {
+ return &m;
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ std::unordered_set<std::string> image_classes_;
+};
+
+inline CompilationHelper::~CompilationHelper() {
+ for (ScratchFile& image_file : image_files) {
+ image_file.Unlink();
+ }
+ for (ScratchFile& oat_file : oat_files) {
+ oat_file.Unlink();
+ }
+ for (ScratchFile& vdex_file : vdex_files) {
+ vdex_file.Unlink();
+ }
+ const int rmdir_result = rmdir(image_dir.c_str());
+ CHECK_EQ(0, rmdir_result);
+}
+
+inline std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() {
+ std::vector<size_t> ret;
+ for (ScratchFile& image_file : image_files) {
+ std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
+ CHECK(file.get() != nullptr);
+ ImageHeader image_header;
+ CHECK_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
+ CHECK(image_header.IsValid());
+ ret.push_back(image_header.GetObjectsSection().Size());
+ }
+ return ret;
+}
+
+inline void CompilationHelper::Compile(CompilerDriver* driver,
+ ImageHeader::StorageMode storage_mode) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ std::vector<const DexFile*> class_path = class_linker->GetBootClassPath();
+
+ for (const std::unique_ptr<const DexFile>& dex_file : extra_dex_files) {
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ // Inject in boot class path so that the compiler driver can see it.
+ class_linker->AppendToBootClassPath(soa.Self(), *dex_file.get());
+ }
+ class_path.push_back(dex_file.get());
+ }
+
+ // Enable write for dex2dex.
+ for (const DexFile* dex_file : class_path) {
+ dex_file_locations.push_back(dex_file->GetLocation());
+ if (dex_file->IsReadOnly()) {
+ dex_file->EnableWrite();
+ }
+ }
+ {
+ // Create a generic tmp file, to be the base of the .art and .oat temporary files.
+ ScratchFile location;
+ for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
+ std::string cur_location =
+ android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
+ image_locations.push_back(ScratchFile(cur_location));
+ }
+ }
+ std::vector<std::string> image_filenames;
+ for (ScratchFile& file : image_locations) {
+ std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA));
+ image_filenames.push_back(image_filename);
+ size_t pos = image_filename.rfind('/');
+ CHECK_NE(pos, std::string::npos) << image_filename;
+ if (image_dir.empty()) {
+ image_dir = image_filename.substr(0, pos);
+ int mkdir_result = mkdir(image_dir.c_str(), 0700);
+ CHECK_EQ(0, mkdir_result) << image_dir;
+ }
+ image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str())));
+ }
+
+ std::vector<std::string> oat_filenames;
+ std::vector<std::string> vdex_filenames;
+ for (const std::string& image_filename : image_filenames) {
+ std::string oat_filename = ReplaceFileExtension(image_filename, "oat");
+ oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str())));
+ oat_filenames.push_back(oat_filename);
+ std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex");
+ vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str())));
+ vdex_filenames.push_back(vdex_filename);
+ }
+
+ std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map;
+ std::vector<const char*> oat_filename_vector;
+ for (const std::string& file : oat_filenames) {
+ oat_filename_vector.push_back(file.c_str());
+ }
+ std::vector<const char*> image_filename_vector;
+ for (const std::string& file : image_filenames) {
+ image_filename_vector.push_back(file.c_str());
+ }
+ size_t image_idx = 0;
+ for (const DexFile* dex_file : class_path) {
+ dex_file_to_oat_index_map.emplace(dex_file, image_idx);
+ ++image_idx;
+ }
+ // TODO: compile_pic should be a test argument.
+ std::unique_ptr<ImageWriter> writer(new ImageWriter(*driver,
+ kRequestedImageBase,
+ /*compile_pic*/false,
+ /*compile_app_image*/false,
+ storage_mode,
+ oat_filename_vector,
+ dex_file_to_oat_index_map,
+ /*dirty_image_objects*/nullptr));
+ {
+ {
+ jobject class_loader = nullptr;
+ TimingLogger timings("ImageTest::WriteRead", false, false);
+ TimingLogger::ScopedTiming t("CompileAll", &timings);
+ driver->SetDexFilesForOatFile(class_path);
+ driver->CompileAll(class_loader, class_path, &timings);
+
+ t.NewTiming("WriteElf");
+ SafeMap<std::string, std::string> key_value_store;
+ std::vector<const char*> dex_filename_vector;
+ for (size_t i = 0; i < class_path.size(); ++i) {
+ dex_filename_vector.push_back("");
+ }
+ key_value_store.Put(OatHeader::kBootClassPathKey,
+ gc::space::ImageSpace::GetMultiImageBootClassPath(
+ dex_filename_vector,
+ oat_filename_vector,
+ image_filename_vector));
+
+ std::vector<std::unique_ptr<ElfWriter>> elf_writers;
+ std::vector<std::unique_ptr<OatWriter>> oat_writers;
+ for (ScratchFile& oat_file : oat_files) {
+ elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(),
+ driver->GetInstructionSetFeatures(),
+ &driver->GetCompilerOptions(),
+ oat_file.GetFile()));
+ elf_writers.back()->Start();
+ oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true,
+ &timings,
+ /*profile_compilation_info*/nullptr));
+ }
+
+ std::vector<OutputStream*> rodata;
+ std::vector<std::unique_ptr<MemMap>> opened_dex_files_map;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+ // Now that we have finalized key_value_store_, start writing the oat file.
+ for (size_t i = 0, size = oat_writers.size(); i != size; ++i) {
+ const DexFile* dex_file = class_path[i];
+ rodata.push_back(elf_writers[i]->StartRoData());
+ ArrayRef<const uint8_t> raw_dex_file(
+ reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
+ dex_file->GetHeader().file_size_);
+ oat_writers[i]->AddRawDexFileSource(raw_dex_file,
+ dex_file->GetLocation().c_str(),
+ dex_file->GetLocationChecksum());
+
+ std::unique_ptr<MemMap> cur_opened_dex_files_map;
+ std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files;
+ bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles(
+ kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(),
+ rodata.back(),
+ driver->GetInstructionSet(),
+ driver->GetInstructionSetFeatures(),
+ &key_value_store,
+ /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify.
+ /* update_input_vdex */ false,
+ &cur_opened_dex_files_map,
+ &cur_opened_dex_files);
+ ASSERT_TRUE(dex_files_ok);
+
+ if (cur_opened_dex_files_map != nullptr) {
+ opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map));
+ for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) {
+ // dex_file_oat_index_map_.emplace(dex_file.get(), i);
+ opened_dex_files.push_back(std::move(cur_dex_file));
+ }
+ } else {
+ ASSERT_TRUE(cur_opened_dex_files.empty());
+ }
+ }
+ bool image_space_ok = writer->PrepareImageAddressSpace();
+ ASSERT_TRUE(image_space_ok);
+
+ if (kIsVdexEnabled) {
+ for (size_t i = 0, size = vdex_files.size(); i != size; ++i) {
+ std::unique_ptr<BufferedOutputStream> vdex_out =
+ std::make_unique<BufferedOutputStream>(
+ std::make_unique<FileOutputStream>(vdex_files[i].GetFile()));
+ oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr);
+ oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get());
+ }
+ }
+
+ for (size_t i = 0, size = oat_files.size(); i != size; ++i) {
+ linker::MultiOatRelativePatcher patcher(driver->GetInstructionSet(),
+ driver->GetInstructionSetFeatures());
+ OatWriter* const oat_writer = oat_writers[i].get();
+ ElfWriter* const elf_writer = elf_writers[i].get();
+ std::vector<const DexFile*> cur_dex_files(1u, class_path[i]);
+ oat_writer->Initialize(driver, writer.get(), cur_dex_files);
+ oat_writer->PrepareLayout(&patcher);
+ size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
+ size_t text_size = oat_writer->GetOatSize() - rodata_size;
+ elf_writer->PrepareDynamicSection(rodata_size,
+ text_size,
+ oat_writer->GetBssSize(),
+ oat_writer->GetBssMethodsOffset(),
+ oat_writer->GetBssRootsOffset());
+
+ writer->UpdateOatFileLayout(i,
+ elf_writer->GetLoadedSize(),
+ oat_writer->GetOatDataOffset(),
+ oat_writer->GetOatSize());
+
+ bool rodata_ok = oat_writer->WriteRodata(rodata[i]);
+ ASSERT_TRUE(rodata_ok);
+ elf_writer->EndRoData(rodata[i]);
+
+ OutputStream* text = elf_writer->StartText();
+ bool text_ok = oat_writer->WriteCode(text);
+ ASSERT_TRUE(text_ok);
+ elf_writer->EndText(text);
+
+ bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u);
+ ASSERT_TRUE(header_ok);
+
+ writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader());
+
+ elf_writer->WriteDynamicSection();
+ elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
+
+ bool success = elf_writer->End();
+ ASSERT_TRUE(success);
+ }
+ }
+
+ bool success_image = writer->Write(kInvalidFd,
+ image_filename_vector,
+ oat_filename_vector);
+ ASSERT_TRUE(success_image);
+
+ for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) {
+ const char* oat_filename = oat_filenames[i].c_str();
+ std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename));
+ ASSERT_TRUE(oat_file != nullptr);
+ bool success_fixup = ElfWriter::Fixup(oat_file.get(),
+ writer->GetOatDataBegin(i));
+ ASSERT_TRUE(success_fixup);
+ ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
+ << oat_filename;
+ }
+ }
+}
+
+inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode,
+ CompilationHelper& helper,
+ const std::string& extra_dex,
+ const std::initializer_list<std::string>& image_classes) {
+ for (const std::string& image_class : image_classes) {
+ image_classes_.insert(image_class);
+ }
+ CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
+ // Set inline filter values.
+ compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+ image_classes_.clear();
+ if (!extra_dex.empty()) {
+ helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
+ }
+ helper.Compile(compiler_driver_.get(), storage_mode);
+ if (image_classes.begin() != image_classes.end()) {
+ // Make sure the class got initialized.
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ for (const std::string& image_class : image_classes) {
+ mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
+ EXPECT_TRUE(klass != nullptr);
+ EXPECT_TRUE(klass->IsInitialized());
+ }
+ }
+}
+
+inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
+ CompilationHelper helper;
+ Compile(storage_mode, /*out*/ helper);
+ std::vector<uint64_t> image_file_sizes;
+ for (ScratchFile& image_file : helper.image_files) {
+ std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
+ ASSERT_TRUE(file.get() != nullptr);
+ ImageHeader image_header;
+ ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
+ ASSERT_TRUE(image_header.IsValid());
+ const auto& bitmap_section = image_header.GetImageBitmapSection();
+ ASSERT_GE(bitmap_section.Offset(), sizeof(image_header));
+ ASSERT_NE(0U, bitmap_section.Size());
+
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ ASSERT_TRUE(heap->HaveContinuousSpaces());
+ gc::space::ContinuousSpace* space = heap->GetNonMovingSpace();
+ ASSERT_FALSE(space->IsImageSpace());
+ ASSERT_TRUE(space != nullptr);
+ ASSERT_TRUE(space->IsMallocSpace());
+ image_file_sizes.push_back(file->GetLength());
+ }
+
+ ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr);
+ std::unordered_set<std::string> image_classes(*compiler_driver_->GetImageClasses());
+
+ // Need to delete the compiler since it has worker threads which are attached to runtime.
+ compiler_driver_.reset();
+
+ // Tear down old runtime before making a new one, clearing out misc state.
+
+ // Remove the reservation of the memory for use to load the image.
+ // Need to do this before we reset the runtime.
+ UnreserveImageSpace();
+
+ helper.extra_dex_files.clear();
+ runtime_.reset();
+ java_lang_dex_file_ = nullptr;
+
+ MemMap::Init();
+
+ RuntimeOptions options;
+ std::string image("-Ximage:");
+ image.append(helper.image_locations[0].GetFilename());
+ options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr)));
+ // By default the compiler this creates will not include patch information.
+ options.push_back(std::make_pair("-Xnorelocate", nullptr));
+
+ if (!Runtime::Create(options, false)) {
+ LOG(FATAL) << "Failed to create runtime";
+ return;
+ }
+ runtime_.reset(Runtime::Current());
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
+ // give it away now and then switch to a more managable ScopedObjectAccess.
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+ ScopedObjectAccess soa(Thread::Current());
+ ASSERT_TRUE(runtime_.get() != nullptr);
+ class_linker_ = runtime_->GetClassLinker();
+
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ ASSERT_TRUE(heap->HasBootImageSpace());
+ ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace());
+
+ // We loaded the runtime with an explicit image, so it must exist.
+ ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size());
+ for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) {
+ std::unique_ptr<const DexFile> dex(
+ LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str()));
+ ASSERT_TRUE(dex != nullptr);
+ uint64_t image_file_size = image_file_sizes[i];
+ gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i];
+ ASSERT_TRUE(image_space != nullptr);
+ if (storage_mode == ImageHeader::kStorageModeUncompressed) {
+ // Uncompressed, image should be smaller than file.
+ ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size);
+ } else if (image_file_size > 16 * KB) {
+ // Compressed, file should be smaller than image. Not really valid for small images.
+ ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize());
+ }
+
+ image_space->VerifyImageAllocations();
+ uint8_t* image_begin = image_space->Begin();
+ uint8_t* image_end = image_space->End();
+ if (i == 0) {
+ // This check is only valid for image 0.
+ CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin));
+ }
+ for (size_t j = 0; j < dex->NumClassDefs(); ++j) {
+ const DexFile::ClassDef& class_def = dex->GetClassDef(j);
+ const char* descriptor = dex->GetClassDescriptor(class_def);
+ mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
+ EXPECT_TRUE(klass != nullptr) << descriptor;
+ if (image_classes.find(descriptor) == image_classes.end()) {
+ EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end ||
+ reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor;
+ } else {
+ // Image classes should be located inside the image.
+ EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor;
+ EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor;
+ }
+ EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false)));
+ }
+ }
+}
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_DEX2OAT_LINKER_IMAGE_TEST_H_
diff --git a/dex2oat/linker/image_write_read_test.cc b/dex2oat/linker/image_write_read_test.cc
new file mode 100644
index 0000000..30996b5
--- /dev/null
+++ b/dex2oat/linker/image_write_read_test.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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 "image_test.h"
+
+namespace art {
+namespace linker {
+
+TEST_F(ImageTest, WriteReadUncompressed) {
+ TestWriteRead(ImageHeader::kStorageModeUncompressed);
+}
+
+TEST_F(ImageTest, WriteReadLZ4) {
+ TestWriteRead(ImageHeader::kStorageModeLZ4);
+}
+
+TEST_F(ImageTest, WriteReadLZ4HC) {
+ TestWriteRead(ImageHeader::kStorageModeLZ4HC);
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
new file mode 100644
index 0000000..ee568e8
--- /dev/null
+++ b/dex2oat/linker/image_writer.cc
@@ -0,0 +1,2839 @@
+/*
+ * Copyright (C) 2011 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 "image_writer.h"
+
+#include <lz4.h>
+#include <lz4hc.h>
+#include <sys/stat.h>
+
+#include <memory>
+#include <numeric>
+#include <unordered_set>
+#include <vector>
+
+#include "art_field-inl.h"
+#include "art_method-inl.h"
+#include "base/callee_save_type.h"
+#include "base/enums.h"
+#include "base/logging.h"
+#include "base/unix_file/fd_file.h"
+#include "class_linker-inl.h"
+#include "compiled_method.h"
+#include "dex_file-inl.h"
+#include "dex_file_types.h"
+#include "driver/compiler_driver.h"
+#include "elf_file.h"
+#include "elf_utils.h"
+#include "gc/accounting/card_table-inl.h"
+#include "gc/accounting/heap_bitmap.h"
+#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/collector/concurrent_copying.h"
+#include "gc/heap-visit-objects-inl.h"
+#include "gc/heap.h"
+#include "gc/space/large_object_space.h"
+#include "gc/space/space-inl.h"
+#include "gc/verification.h"
+#include "globals.h"
+#include "handle_scope-inl.h"
+#include "image.h"
+#include "imt_conflict_table.h"
+#include "jni_internal.h"
+#include "linear_alloc.h"
+#include "lock_word.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_ext.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/executable.h"
+#include "mirror/method.h"
+#include "mirror/object-inl.h"
+#include "mirror/object-refvisitor-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/string-inl.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "oat_file_manager.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+#include "well_known_classes.h"
+
+using ::art::mirror::Class;
+using ::art::mirror::DexCache;
+using ::art::mirror::Object;
+using ::art::mirror::ObjectArray;
+using ::art::mirror::String;
+
+namespace art {
+namespace linker {
+
+// Separate objects into multiple bins to optimize dirty memory use.
+static constexpr bool kBinObjects = true;
+
+// Return true if an object is already in an image space.
+bool ImageWriter::IsInBootImage(const void* obj) const {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ if (!compile_app_image_) {
+ DCHECK(heap->GetBootImageSpaces().empty());
+ return false;
+ }
+ for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) {
+ const uint8_t* image_begin = boot_image_space->Begin();
+ // Real image end including ArtMethods and ArtField sections.
+ const uint8_t* image_end = image_begin + boot_image_space->GetImageHeader().GetImageSize();
+ if (image_begin <= obj && obj < image_end) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ImageWriter::IsInBootOatFile(const void* ptr) const {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ if (!compile_app_image_) {
+ DCHECK(heap->GetBootImageSpaces().empty());
+ return false;
+ }
+ for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) {
+ const ImageHeader& image_header = boot_image_space->GetImageHeader();
+ if (image_header.GetOatFileBegin() <= ptr && ptr < image_header.GetOatFileEnd()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void ClearDexFileCookies() REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto visitor = [](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(obj != nullptr);
+ Class* klass = obj->GetClass();
+ if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) {
+ ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ // Null out the cookie to enable determinism. b/34090128
+ field->SetObject</*kTransactionActive*/false>(obj, nullptr);
+ }
+ };
+ Runtime::Current()->GetHeap()->VisitObjects(visitor);
+}
+
+bool ImageWriter::PrepareImageAddressSpace() {
+ target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet());
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ PruneNonImageClasses(); // Remove junk
+ if (compile_app_image_) {
+ // Clear dex file cookies for app images to enable app image determinism. This is required
+ // since the cookie field contains long pointers to DexFiles which are not deterministic.
+ // b/34090128
+ ClearDexFileCookies();
+ } else {
+ // Avoid for app image since this may increase RAM and image size.
+ ComputeLazyFieldsForImageClasses(); // Add useful information
+ }
+ }
+ heap->CollectGarbage(false); // Remove garbage.
+
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(Thread::Current());
+ CheckNonImageClassesRemoved();
+ }
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ CalculateNewObjectOffsets();
+ }
+
+ // This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and
+ // bin size sums being calculated.
+ if (!AllocMemory()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ImageWriter::Write(int image_fd,
+ const std::vector<const char*>& image_filenames,
+ const std::vector<const char*>& oat_filenames) {
+ // If image_fd or oat_fd are not kInvalidFd then we may have empty strings in image_filenames or
+ // oat_filenames.
+ CHECK(!image_filenames.empty());
+ if (image_fd != kInvalidFd) {
+ CHECK_EQ(image_filenames.size(), 1u);
+ }
+ CHECK(!oat_filenames.empty());
+ CHECK_EQ(image_filenames.size(), oat_filenames.size());
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ for (size_t i = 0; i < oat_filenames.size(); ++i) {
+ CreateHeader(i);
+ CopyAndFixupNativeData(i);
+ }
+ }
+
+ {
+ // TODO: heap validation can't handle these fix up passes.
+ ScopedObjectAccess soa(Thread::Current());
+ Runtime::Current()->GetHeap()->DisableObjectValidation();
+ CopyAndFixupObjects();
+ }
+
+ for (size_t i = 0; i < image_filenames.size(); ++i) {
+ const char* image_filename = image_filenames[i];
+ ImageInfo& image_info = GetImageInfo(i);
+ std::unique_ptr<File> image_file;
+ if (image_fd != kInvalidFd) {
+ if (strlen(image_filename) == 0u) {
+ image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage));
+ // Empty the file in case it already exists.
+ if (image_file != nullptr) {
+ TEMP_FAILURE_RETRY(image_file->SetLength(0));
+ TEMP_FAILURE_RETRY(image_file->Flush());
+ }
+ } else {
+ LOG(ERROR) << "image fd " << image_fd << " name " << image_filename;
+ }
+ } else {
+ image_file.reset(OS::CreateEmptyFile(image_filename));
+ }
+
+ if (image_file == nullptr) {
+ LOG(ERROR) << "Failed to open image file " << image_filename;
+ return false;
+ }
+
+ if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
+ image_file->Erase();
+ return EXIT_FAILURE;
+ }
+
+ std::unique_ptr<char[]> compressed_data;
+ // Image data size excludes the bitmap and the header.
+ ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
+ const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader);
+ char* image_data = reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader);
+ size_t data_size;
+ const char* image_data_to_write;
+ const uint64_t compress_start_time = NanoTime();
+
+ CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
+ switch (image_storage_mode_) {
+ case ImageHeader::kStorageModeLZ4HC: // Fall-through.
+ case ImageHeader::kStorageModeLZ4: {
+ const size_t compressed_max_size = LZ4_compressBound(image_data_size);
+ compressed_data.reset(new char[compressed_max_size]);
+ data_size = LZ4_compress_default(
+ reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
+ &compressed_data[0],
+ image_data_size,
+ compressed_max_size);
+
+ break;
+ }
+ /*
+ * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444
+ case ImageHeader::kStorageModeLZ4HC: {
+ // Bound is same as non HC.
+ const size_t compressed_max_size = LZ4_compressBound(image_data_size);
+ compressed_data.reset(new char[compressed_max_size]);
+ data_size = LZ4_compressHC(
+ reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
+ &compressed_data[0],
+ image_data_size);
+ break;
+ }
+ */
+ case ImageHeader::kStorageModeUncompressed: {
+ data_size = image_data_size;
+ image_data_to_write = image_data;
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
+ }
+ }
+
+ if (compressed_data != nullptr) {
+ image_data_to_write = &compressed_data[0];
+ VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in "
+ << PrettyDuration(NanoTime() - compress_start_time);
+ if (kIsDebugBuild) {
+ std::unique_ptr<uint8_t[]> temp(new uint8_t[image_data_size]);
+ const size_t decompressed_size = LZ4_decompress_safe(
+ reinterpret_cast<char*>(&compressed_data[0]),
+ reinterpret_cast<char*>(&temp[0]),
+ data_size,
+ image_data_size);
+ CHECK_EQ(decompressed_size, image_data_size);
+ CHECK_EQ(memcmp(image_data, &temp[0], image_data_size), 0) << image_storage_mode_;
+ }
+ }
+
+ // Write out the image + fields + methods.
+ const bool is_compressed = compressed_data != nullptr;
+ if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) {
+ PLOG(ERROR) << "Failed to write image file data " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write out the image bitmap at the page aligned start of the image end, also uncompressed for
+ // convenience.
+ const ImageSection& bitmap_section = image_header->GetImageBitmapSection();
+ // Align up since data size may be unaligned if the image is compressed.
+ size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize);
+ if (!is_compressed) {
+ CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
+ }
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
+ bitmap_section.Size(),
+ bitmap_position_in_file)) {
+ PLOG(ERROR) << "Failed to write image file " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
+ int err = image_file->Flush();
+ if (err < 0) {
+ PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write header last in case the compiler gets killed in the middle of image writing.
+ // We do not want to have a corrupted image with a valid header.
+ // The header is uncompressed since it contains whether the image is compressed or not.
+ image_header->data_size_ = data_size;
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()),
+ sizeof(ImageHeader),
+ 0)) {
+ PLOG(ERROR) << "Failed to write image file header " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
+ CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
+ static_cast<size_t>(image_file->GetLength()));
+ if (image_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
+ return false;
+ }
+ }
+ return true;
+}
+
+void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) {
+ DCHECK(object != nullptr);
+ DCHECK_NE(offset, 0U);
+
+ // The object is already deflated from when we set the bin slot. Just overwrite the lock word.
+ object->SetLockWord(LockWord::FromForwardingAddress(offset), false);
+ DCHECK_EQ(object->GetLockWord(false).ReadBarrierState(), 0u);
+ DCHECK(IsImageOffsetAssigned(object));
+}
+
+void ImageWriter::UpdateImageOffset(mirror::Object* obj, uintptr_t offset) {
+ DCHECK(IsImageOffsetAssigned(obj)) << obj << " " << offset;
+ obj->SetLockWord(LockWord::FromForwardingAddress(offset), false);
+ DCHECK_EQ(obj->GetLockWord(false).ReadBarrierState(), 0u);
+}
+
+void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot bin_slot) {
+ DCHECK(object != nullptr);
+ DCHECK_NE(image_objects_offset_begin_, 0u);
+
+ size_t oat_index = GetOatIndex(object);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()];
+ size_t new_offset = bin_slot_offset + bin_slot.GetIndex();
+ DCHECK_ALIGNED(new_offset, kObjectAlignment);
+
+ SetImageOffset(object, new_offset);
+ DCHECK_LT(new_offset, image_info.image_end_);
+}
+
+bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const {
+ // Will also return true if the bin slot was assigned since we are reusing the lock word.
+ DCHECK(object != nullptr);
+ return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress;
+}
+
+size_t ImageWriter::GetImageOffset(mirror::Object* object) const {
+ DCHECK(object != nullptr);
+ DCHECK(IsImageOffsetAssigned(object));
+ LockWord lock_word = object->GetLockWord(false);
+ size_t offset = lock_word.ForwardingAddress();
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
+ DCHECK_LT(offset, image_info.image_end_);
+ return offset;
+}
+
+void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) {
+ DCHECK(object != nullptr);
+ DCHECK(!IsImageOffsetAssigned(object));
+ DCHECK(!IsImageBinSlotAssigned(object));
+
+ // Before we stomp over the lock word, save the hash code for later.
+ LockWord lw(object->GetLockWord(false));
+ switch (lw.GetState()) {
+ case LockWord::kFatLocked:
+ FALLTHROUGH_INTENDED;
+ case LockWord::kThinLocked: {
+ std::ostringstream oss;
+ bool thin = (lw.GetState() == LockWord::kThinLocked);
+ oss << (thin ? "Thin" : "Fat")
+ << " locked object " << object << "(" << object->PrettyTypeOf()
+ << ") found during object copy";
+ if (thin) {
+ oss << ". Lock owner:" << lw.ThinLockOwner();
+ }
+ LOG(FATAL) << oss.str();
+ break;
+ }
+ case LockWord::kUnlocked:
+ // No hash, don't need to save it.
+ break;
+ case LockWord::kHashCode:
+ DCHECK(saved_hashcode_map_.find(object) == saved_hashcode_map_.end());
+ saved_hashcode_map_.emplace(object, lw.GetHashCode());
+ break;
+ default:
+ LOG(FATAL) << "Unreachable.";
+ UNREACHABLE();
+ }
+ object->SetLockWord(LockWord::FromForwardingAddress(bin_slot.Uint32Value()), false);
+ DCHECK_EQ(object->GetLockWord(false).ReadBarrierState(), 0u);
+ DCHECK(IsImageBinSlotAssigned(object));
+}
+
+void ImageWriter::PrepareDexCacheArraySlots() {
+ // Prepare dex cache array starts based on the ordering specified in the CompilerDriver.
+ // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
+ // when AssignImageBinSlot() assigns their indexes out or order.
+ for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) {
+ auto it = dex_file_oat_index_map_.find(dex_file);
+ DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
+ ImageInfo& image_info = GetImageInfo(it->second);
+ image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]);
+ DexCacheArraysLayout layout(target_ptr_size_, dex_file);
+ image_info.bin_slot_sizes_[kBinDexCacheArray] += layout.Size();
+ }
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ ObjPtr<mirror::DexCache> dex_cache =
+ ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
+ if (dex_cache == nullptr || IsInBootImage(dex_cache.Ptr())) {
+ continue;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ CHECK(dex_file_oat_index_map_.find(dex_file) != dex_file_oat_index_map_.end())
+ << "Dex cache should have been pruned " << dex_file->GetLocation()
+ << "; possibly in class path";
+ DexCacheArraysLayout layout(target_ptr_size_, dex_file);
+ DCHECK(layout.Valid());
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file);
+ DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(),
+ start + layout.TypesOffset(),
+ dex_cache);
+ DCHECK_EQ(dex_file->NumMethodIds() != 0u, dex_cache->GetResolvedMethods() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(),
+ start + layout.MethodsOffset(),
+ dex_cache);
+ DCHECK_EQ(dex_file->NumFieldIds() != 0u, dex_cache->GetResolvedFields() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(),
+ start + layout.FieldsOffset(),
+ dex_cache);
+ DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset(), dex_cache);
+
+ if (dex_cache->GetResolvedMethodTypes() != nullptr) {
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedMethodTypes(),
+ start + layout.MethodTypesOffset(),
+ dex_cache);
+ }
+ if (dex_cache->GetResolvedCallSites() != nullptr) {
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedCallSites(),
+ start + layout.CallSitesOffset(),
+ dex_cache);
+ }
+ }
+}
+
+void ImageWriter::AddDexCacheArrayRelocation(void* array,
+ size_t offset,
+ ObjPtr<mirror::DexCache> dex_cache) {
+ if (array != nullptr) {
+ DCHECK(!IsInBootImage(array));
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
+ native_object_relocations_.emplace(array,
+ NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray });
+ }
+}
+
+void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) {
+ DCHECK(arr != nullptr);
+ if (kIsDebugBuild) {
+ for (size_t i = 0, len = arr->GetLength(); i < len; i++) {
+ ArtMethod* method = arr->GetElementPtrSize<ArtMethod*>(i, target_ptr_size_);
+ if (method != nullptr && !method->IsRuntimeMethod()) {
+ mirror::Class* klass = method->GetDeclaringClass();
+ CHECK(klass == nullptr || KeepClass(klass))
+ << Class::PrettyClass(klass) << " should be a kept class";
+ }
+ }
+ }
+ // kBinArtMethodClean picked arbitrarily, just required to differentiate between ArtFields and
+ // ArtMethods.
+ pointer_arrays_.emplace(arr, kBinArtMethodClean);
+}
+
+void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) {
+ DCHECK(object != nullptr);
+ size_t object_size = object->SizeOf();
+
+ // The magic happens here. We segregate objects into different bins based
+ // on how likely they are to get dirty at runtime.
+ //
+ // Likely-to-dirty objects get packed together into the same bin so that
+ // at runtime their page dirtiness ratio (how many dirty objects a page has) is
+ // maximized.
+ //
+ // This means more pages will stay either clean or shared dirty (with zygote) and
+ // the app will use less of its own (private) memory.
+ Bin bin = kBinRegular;
+ size_t current_offset = 0u;
+
+ if (kBinObjects) {
+ //
+ // Changing the bin of an object is purely a memory-use tuning.
+ // It has no change on runtime correctness.
+ //
+ // Memory analysis has determined that the following types of objects get dirtied
+ // the most:
+ //
+ // * Dex cache arrays are stored in a special bin. The arrays for each dex cache have
+ // a fixed layout which helps improve generated code (using PC-relative addressing),
+ // so we pre-calculate their offsets separately in PrepareDexCacheArraySlots().
+ // Since these arrays are huge, most pages do not overlap other objects and it's not
+ // really important where they are for the clean/dirty separation. Due to their
+ // special PC-relative addressing, we arbitrarily keep them at the end.
+ // * Class'es which are verified [their clinit runs only at runtime]
+ // - classes in general [because their static fields get overwritten]
+ // - initialized classes with all-final statics are unlikely to be ever dirty,
+ // so bin them separately
+ // * Art Methods that are:
+ // - native [their native entry point is not looked up until runtime]
+ // - have declaring classes that aren't initialized
+ // [their interpreter/quick entry points are trampolines until the class
+ // becomes initialized]
+ //
+ // We also assume the following objects get dirtied either never or extremely rarely:
+ // * Strings (they are immutable)
+ // * Art methods that aren't native and have initialized declared classes
+ //
+ // We assume that "regular" bin objects are highly unlikely to become dirtied,
+ // so packing them together will not result in a noticeably tighter dirty-to-clean ratio.
+ //
+ if (object->IsClass()) {
+ bin = kBinClassVerified;
+ mirror::Class* klass = object->AsClass();
+
+ // Add non-embedded vtable to the pointer array table if there is one.
+ auto* vtable = klass->GetVTable();
+ if (vtable != nullptr) {
+ AddMethodPointerArray(vtable);
+ }
+ auto* iftable = klass->GetIfTable();
+ if (iftable != nullptr) {
+ for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
+ if (iftable->GetMethodArrayCount(i) > 0) {
+ AddMethodPointerArray(iftable->GetMethodArray(i));
+ }
+ }
+ }
+
+ // Move known dirty objects into their own sections. This includes:
+ // - classes with dirty static fields.
+ if (dirty_image_objects_ != nullptr &&
+ dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) {
+ bin = kBinKnownDirty;
+ } else if (klass->GetStatus() == Class::kStatusInitialized) {
+ bin = kBinClassInitialized;
+
+ // If the class's static fields are all final, put it into a separate bin
+ // since it's very likely it will stay clean.
+ uint32_t num_static_fields = klass->NumStaticFields();
+ if (num_static_fields == 0) {
+ bin = kBinClassInitializedFinalStatics;
+ } else {
+ // Maybe all the statics are final?
+ bool all_final = true;
+ for (uint32_t i = 0; i < num_static_fields; ++i) {
+ ArtField* field = klass->GetStaticField(i);
+ if (!field->IsFinal()) {
+ all_final = false;
+ break;
+ }
+ }
+
+ if (all_final) {
+ bin = kBinClassInitializedFinalStatics;
+ }
+ }
+ }
+ } else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
+ bin = kBinString; // Strings are almost always immutable (except for object header).
+ } else if (object->GetClass<kVerifyNone>() ==
+ Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangObject)) {
+ // Instance of java lang object, probably a lock object. This means it will be dirty when we
+ // synchronize on it.
+ bin = kBinMiscDirty;
+ } else if (object->IsDexCache()) {
+ // Dex file field becomes dirty when the image is loaded.
+ bin = kBinMiscDirty;
+ }
+ // else bin = kBinRegular
+ }
+
+ // Assign the oat index too.
+ DCHECK(oat_index_map_.find(object) == oat_index_map_.end());
+ oat_index_map_.emplace(object, oat_index);
+
+ ImageInfo& image_info = GetImageInfo(oat_index);
+
+ size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment
+ current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
+ // Move the current bin size up to accommodate the object we just assigned a bin slot.
+ image_info.bin_slot_sizes_[bin] += offset_delta;
+
+ BinSlot new_bin_slot(bin, current_offset);
+ SetImageBinSlot(object, new_bin_slot);
+
+ ++image_info.bin_slot_count_[bin];
+
+ // Grow the image closer to the end by the object we just assigned.
+ image_info.image_end_ += offset_delta;
+}
+
+bool ImageWriter::WillMethodBeDirty(ArtMethod* m) const {
+ if (m->IsNative()) {
+ return true;
+ }
+ mirror::Class* declaring_class = m->GetDeclaringClass();
+ // Initialized is highly unlikely to dirty since there's no entry points to mutate.
+ return declaring_class == nullptr || declaring_class->GetStatus() != Class::kStatusInitialized;
+}
+
+bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const {
+ DCHECK(object != nullptr);
+
+ // We always stash the bin slot into a lockword, in the 'forwarding address' state.
+ // If it's in some other state, then we haven't yet assigned an image bin slot.
+ if (object->GetLockWord(false).GetState() != LockWord::kForwardingAddress) {
+ return false;
+ } else if (kIsDebugBuild) {
+ LockWord lock_word = object->GetLockWord(false);
+ size_t offset = lock_word.ForwardingAddress();
+ BinSlot bin_slot(offset);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
+ DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()])
+ << "bin slot offset should not exceed the size of that bin";
+ }
+ return true;
+}
+
+ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const {
+ DCHECK(object != nullptr);
+ DCHECK(IsImageBinSlotAssigned(object));
+
+ LockWord lock_word = object->GetLockWord(false);
+ size_t offset = lock_word.ForwardingAddress(); // TODO: ForwardingAddress should be uint32_t
+ DCHECK_LE(offset, std::numeric_limits<uint32_t>::max());
+
+ BinSlot bin_slot(static_cast<uint32_t>(offset));
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
+ DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]);
+
+ return bin_slot;
+}
+
+bool ImageWriter::AllocMemory() {
+ for (ImageInfo& image_info : image_infos_) {
+ ImageSection unused_sections[ImageHeader::kSectionCount];
+ const size_t length = RoundUp(
+ image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize);
+
+ std::string error_msg;
+ image_info.image_.reset(MemMap::MapAnonymous("image writer image",
+ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ false,
+ false,
+ &error_msg));
+ if (UNLIKELY(image_info.image_.get() == nullptr)) {
+ LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
+ return false;
+ }
+
+ // Create the image bitmap, only needs to cover mirror object section which is up to image_end_.
+ CHECK_LE(image_info.image_end_, length);
+ image_info.image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create(
+ "image bitmap", image_info.image_->Begin(), RoundUp(image_info.image_end_, kPageSize)));
+ if (image_info.image_bitmap_.get() == nullptr) {
+ LOG(ERROR) << "Failed to allocate memory for image bitmap";
+ return false;
+ }
+ }
+ return true;
+}
+
+class ImageWriter::ComputeLazyFieldsForClassesVisitor : public ClassVisitor {
+ public:
+ bool operator()(ObjPtr<Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(Thread::Current());
+ mirror::Class::ComputeName(hs.NewHandle(c));
+ return true;
+ }
+};
+
+void ImageWriter::ComputeLazyFieldsForImageClasses() {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ComputeLazyFieldsForClassesVisitor visitor;
+ class_linker->VisitClassesWithoutClassesLock(&visitor);
+}
+
+static bool IsBootClassLoaderClass(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return klass->GetClassLoader() == nullptr;
+}
+
+bool ImageWriter::IsBootClassLoaderNonImageClass(mirror::Class* klass) {
+ return IsBootClassLoaderClass(klass) && !IsInBootImage(klass);
+}
+
+// This visitor follows the references of an instance, recursively then prune this class
+// if a type of any field is pruned.
+class ImageWriter::PruneObjectReferenceVisitor {
+ public:
+ PruneObjectReferenceVisitor(ImageWriter* image_writer,
+ bool* early_exit,
+ std::unordered_set<mirror::Object*>* visited,
+ bool* result)
+ : image_writer_(image_writer), early_exit_(early_exit), visited_(visited), result_(result) {}
+
+ ALWAYS_INLINE void VisitRootIfNonNull(
+ mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) { }
+
+ ALWAYS_INLINE void VisitRoot(
+ mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) { }
+
+ ALWAYS_INLINE void operator() (ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Object* ref =
+ obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
+ if (ref == nullptr || visited_->find(ref) != visited_->end()) {
+ return;
+ }
+
+ ObjPtr<mirror::Class> klass = ref->IsClass() ? ref->AsClass() : ref->GetClass();
+ if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) {
+ // Prune all classes using reflection because the content they held will not be fixup.
+ *result_ = true;
+ }
+
+ if (ref->IsClass()) {
+ *result_ = *result_ ||
+ image_writer_->PruneAppImageClassInternal(ref->AsClass(), early_exit_, visited_);
+ } else {
+ // Record the object visited in case of circular reference.
+ visited_->emplace(ref);
+ *result_ = *result_ ||
+ image_writer_->PruneAppImageClassInternal(klass, early_exit_, visited_);
+ ref->VisitReferences(*this, *this);
+ // Clean up before exit for next call of this function.
+ visited_->erase(ref);
+ }
+ }
+
+ ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
+ }
+
+ ALWAYS_INLINE bool GetResult() const {
+ return result_;
+ }
+
+ private:
+ ImageWriter* image_writer_;
+ bool* early_exit_;
+ std::unordered_set<mirror::Object*>* visited_;
+ bool* const result_;
+};
+
+
+bool ImageWriter::PruneAppImageClass(ObjPtr<mirror::Class> klass) {
+ bool early_exit = false;
+ std::unordered_set<mirror::Object*> visited;
+ return PruneAppImageClassInternal(klass, &early_exit, &visited);
+}
+
+bool ImageWriter::PruneAppImageClassInternal(
+ ObjPtr<mirror::Class> klass,
+ bool* early_exit,
+ std::unordered_set<mirror::Object*>* visited) {
+ DCHECK(early_exit != nullptr);
+ DCHECK(visited != nullptr);
+ DCHECK(compile_app_image_);
+ if (klass == nullptr || IsInBootImage(klass.Ptr())) {
+ return false;
+ }
+ auto found = prune_class_memo_.find(klass.Ptr());
+ if (found != prune_class_memo_.end()) {
+ // Already computed, return the found value.
+ return found->second;
+ }
+ // Circular dependencies, return false but do not store the result in the memoization table.
+ if (visited->find(klass.Ptr()) != visited->end()) {
+ *early_exit = true;
+ return false;
+ }
+ visited->emplace(klass.Ptr());
+ bool result = IsBootClassLoaderClass(klass);
+ std::string temp;
+ // Prune if not an image class, this handles any broken sets of image classes such as having a
+ // class in the set but not it's superclass.
+ result = result || !compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
+ bool my_early_exit = false; // Only for ourselves, ignore caller.
+ // Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the
+ // app image.
+ if (klass->IsErroneous()) {
+ result = true;
+ } else {
+ ObjPtr<mirror::ClassExt> ext(klass->GetExtData());
+ CHECK(ext.IsNull() || ext->GetVerifyError() == nullptr) << klass->PrettyClass();
+ }
+ if (!result) {
+ // Check interfaces since these wont be visited through VisitReferences.)
+ mirror::IfTable* if_table = klass->GetIfTable();
+ for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
+ result = result || PruneAppImageClassInternal(if_table->GetInterface(i),
+ &my_early_exit,
+ visited);
+ }
+ }
+ if (klass->IsObjectArrayClass()) {
+ result = result || PruneAppImageClassInternal(klass->GetComponentType(),
+ &my_early_exit,
+ visited);
+ }
+ // Check static fields and their classes.
+ if (klass->IsResolved() && klass->NumReferenceStaticFields() != 0) {
+ size_t num_static_fields = klass->NumReferenceStaticFields();
+ // Presumably GC can happen when we are cross compiling, it should not cause performance
+ // problems to do pointer size logic.
+ MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset(
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ for (size_t i = 0u; i < num_static_fields; ++i) {
+ mirror::Object* ref = klass->GetFieldObject<mirror::Object>(field_offset);
+ if (ref != nullptr) {
+ if (ref->IsClass()) {
+ result = result || PruneAppImageClassInternal(ref->AsClass(),
+ &my_early_exit,
+ visited);
+ } else {
+ mirror::Class* type = ref->GetClass();
+ result = result || PruneAppImageClassInternal(type,
+ &my_early_exit,
+ visited);
+ if (!result) {
+ // For non-class case, also go through all the types mentioned by it's fields'
+ // references recursively to decide whether to keep this class.
+ bool tmp = false;
+ PruneObjectReferenceVisitor visitor(this, &my_early_exit, visited, &tmp);
+ ref->VisitReferences(visitor, visitor);
+ result = result || tmp;
+ }
+ }
+ }
+ field_offset = MemberOffset(field_offset.Uint32Value() +
+ sizeof(mirror::HeapReference<mirror::Object>));
+ }
+ }
+ result = result || PruneAppImageClassInternal(klass->GetSuperClass(),
+ &my_early_exit,
+ visited);
+ // Remove the class if the dex file is not in the set of dex files. This happens for classes that
+ // are from uses-library if there is no profile. b/30688277
+ mirror::DexCache* dex_cache = klass->GetDexCache();
+ if (dex_cache != nullptr) {
+ result = result ||
+ dex_file_oat_index_map_.find(dex_cache->GetDexFile()) == dex_file_oat_index_map_.end();
+ }
+ // Erase the element we stored earlier since we are exiting the function.
+ auto it = visited->find(klass.Ptr());
+ DCHECK(it != visited->end());
+ visited->erase(it);
+ // Only store result if it is true or none of the calls early exited due to circular
+ // dependencies. If visited is empty then we are the root caller, in this case the cycle was in
+ // a child call and we can remember the result.
+ if (result == true || !my_early_exit || visited->empty()) {
+ prune_class_memo_[klass.Ptr()] = result;
+ }
+ *early_exit |= my_early_exit;
+ return result;
+}
+
+bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) {
+ if (klass == nullptr) {
+ return false;
+ }
+ if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ // Already in boot image, return true.
+ return true;
+ }
+ std::string temp;
+ if (!compiler_driver_.IsImageClass(klass->GetDescriptor(&temp))) {
+ return false;
+ }
+ if (compile_app_image_) {
+ // For app images, we need to prune boot loader classes that are not in the boot image since
+ // these may have already been loaded when the app image is loaded.
+ // Keep classes in the boot image space since we don't want to re-resolve these.
+ return !PruneAppImageClass(klass);
+ }
+ return true;
+}
+
+class ImageWriter::PruneClassesVisitor : public ClassVisitor {
+ public:
+ PruneClassesVisitor(ImageWriter* image_writer, ObjPtr<mirror::ClassLoader> class_loader)
+ : image_writer_(image_writer),
+ class_loader_(class_loader),
+ classes_to_prune_(),
+ defined_class_count_(0u) { }
+
+ bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!image_writer_->KeepClass(klass.Ptr())) {
+ classes_to_prune_.insert(klass.Ptr());
+ if (klass->GetClassLoader() == class_loader_) {
+ ++defined_class_count_;
+ }
+ }
+ return true;
+ }
+
+ size_t Prune() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassTable* class_table =
+ Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader_);
+ for (mirror::Class* klass : classes_to_prune_) {
+ std::string storage;
+ const char* descriptor = klass->GetDescriptor(&storage);
+ bool result = class_table->Remove(descriptor);
+ DCHECK(result);
+ DCHECK(!class_table->Remove(descriptor)) << descriptor;
+ }
+ return defined_class_count_;
+ }
+
+ private:
+ ImageWriter* const image_writer_;
+ const ObjPtr<mirror::ClassLoader> class_loader_;
+ std::unordered_set<mirror::Class*> classes_to_prune_;
+ size_t defined_class_count_;
+};
+
+class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor {
+ public:
+ explicit PruneClassLoaderClassesVisitor(ImageWriter* image_writer)
+ : image_writer_(image_writer), removed_class_count_(0) {}
+
+ virtual void Visit(ObjPtr<mirror::ClassLoader> class_loader) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PruneClassesVisitor classes_visitor(image_writer_, class_loader);
+ ClassTable* class_table =
+ Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader);
+ class_table->Visit(classes_visitor);
+ removed_class_count_ += classes_visitor.Prune();
+
+ // Record app image class loader. The fake boot class loader should not get registered
+ // and we should end up with only one class loader for an app and none for boot image.
+ if (class_loader != nullptr && class_table != nullptr) {
+ DCHECK(class_loader_ == nullptr);
+ class_loader_ = class_loader;
+ }
+ }
+
+ size_t GetRemovedClassCount() const {
+ return removed_class_count_;
+ }
+
+ ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return class_loader_;
+ }
+
+ private:
+ ImageWriter* const image_writer_;
+ size_t removed_class_count_;
+ ObjPtr<mirror::ClassLoader> class_loader_;
+};
+
+void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) {
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ visitor->Visit(nullptr); // Visit boot class loader.
+ Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor);
+}
+
+void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
+ // To ensure deterministic contents of the hash-based arrays, each slot shall contain
+ // the candidate with the lowest index. As we're processing entries in increasing index
+ // order, this means trying to look up the entry for the current index if the slot is
+ // empty or if it contains a higher index.
+
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ // Prune methods.
+ mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
+ dex::TypeIndex last_class_idx; // Initialized to invalid index.
+ ObjPtr<mirror::Class> last_class = nullptr;
+ for (size_t i = 0, num = dex_cache->GetDexFile()->NumMethodIds(); i != num; ++i) {
+ uint32_t slot_idx = dex_cache->MethodSlotIndex(i);
+ auto pair =
+ mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtMethod* method = pair.object;
+ if (method != nullptr && i > stored_index) {
+ continue; // Already checked.
+ }
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a MethodId
+ // results in resolving the referenced class and that can for example throw OOME.
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(i);
+ if (method_id.class_idx_ != last_class_idx) {
+ last_class_idx = method_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(
+ dex_file, last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (method == nullptr || i < stored_index) {
+ if (last_class != nullptr) {
+ const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
+ Signature signature = dex_file.GetMethodSignature(method_id);
+ if (last_class->IsInterface()) {
+ method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_);
+ } else {
+ method = last_class->FindClassMethod(name, signature, target_ptr_size_);
+ }
+ if (method != nullptr) {
+ // If the referenced class is in the image, the defining class must also be there.
+ DCHECK(KeepClass(method->GetDeclaringClass()));
+ dex_cache->SetResolvedMethod(i, method, target_ptr_size_);
+ }
+ }
+ } else {
+ DCHECK_EQ(i, stored_index);
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
+ }
+ }
+ }
+ // Prune fields and make the contents of the field array deterministic.
+ mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
+ last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
+ last_class = nullptr;
+ for (size_t i = 0, end = dex_file.NumFieldIds(); i < end; ++i) {
+ uint32_t slot_idx = dex_cache->FieldSlotIndex(i);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtField* field = pair.object;
+ if (field != nullptr && i > stored_index) {
+ continue; // Already checked.
+ }
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a FieldId
+ // results in resolving the referenced class and that can for example throw OOME.
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(i);
+ if (field_id.class_idx_ != last_class_idx) {
+ last_class_idx = field_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(
+ dex_file, last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (field == nullptr || i < stored_index) {
+ if (last_class != nullptr) {
+ const char* name = dex_file.StringDataByIdx(field_id.name_idx_);
+ const char* type = dex_file.StringByTypeIdx(field_id.type_idx_);
+ field = mirror::Class::FindField(Thread::Current(), last_class, name, type);
+ if (field != nullptr) {
+ // If the referenced class is in the image, the defining class must also be there.
+ DCHECK(KeepClass(field->GetDeclaringClass()));
+ dex_cache->SetResolvedField(i, field, target_ptr_size_);
+ }
+ }
+ } else {
+ DCHECK_EQ(i, stored_index);
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
+ }
+ }
+ }
+ // Prune types and make the contents of the type array deterministic.
+ // This is done after fields and methods as their lookup can touch the types array.
+ for (size_t i = 0, end = dex_cache->GetDexFile()->NumTypeIds(); i < end; ++i) {
+ dex::TypeIndex type_idx(i);
+ uint32_t slot_idx = dex_cache->TypeSlotIndex(type_idx);
+ mirror::TypeDexCachePair pair =
+ dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed);
+ uint32_t stored_index = pair.index;
+ ObjPtr<mirror::Class> klass = pair.object.Read();
+ if (klass == nullptr || i < stored_index) {
+ klass = class_linker->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader);
+ if (klass != nullptr) {
+ DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass);
+ stored_index = i; // For correct clearing below if not keeping the `klass`.
+ }
+ } else if (i == stored_index && !KeepClass(klass)) {
+ dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+ }
+ }
+ // Strings do not need pruning, but the contents of the string array must be deterministic.
+ for (size_t i = 0, end = dex_cache->GetDexFile()->NumStringIds(); i < end; ++i) {
+ dex::StringIndex string_idx(i);
+ uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx);
+ mirror::StringDexCachePair pair =
+ dex_cache->GetStrings()[slot_idx].load(std::memory_order_relaxed);
+ uint32_t stored_index = pair.index;
+ ObjPtr<mirror::String> string = pair.object.Read();
+ if (string == nullptr || i < stored_index) {
+ string = class_linker->LookupString(dex_file, string_idx, dex_cache);
+ DCHECK(string == nullptr || dex_cache->GetResolvedString(string_idx) == string);
+ }
+ }
+}
+
+void ImageWriter::PruneNonImageClasses() {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedAssertNoThreadSuspension sa(__FUNCTION__);
+
+ // Prune uses-library dex caches. Only prune the uses-library dex caches since we want to make
+ // sure the other ones don't get unloaded before the OatWriter runs.
+ class_linker->VisitClassTables(
+ [&](ClassTable* table) REQUIRES_SHARED(Locks::mutator_lock_) {
+ table->RemoveStrongRoots(
+ [&](GcRoot<mirror::Object> root) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> obj = root.Read();
+ if (obj->IsDexCache()) {
+ // Return true if the dex file is not one of the ones in the map.
+ return dex_file_oat_index_map_.find(obj->AsDexCache()->GetDexFile()) ==
+ dex_file_oat_index_map_.end();
+ }
+ // Return false to avoid removing.
+ return false;
+ });
+ });
+
+ // Remove the undesired classes from the class roots.
+ ObjPtr<mirror::ClassLoader> class_loader;
+ {
+ PruneClassLoaderClassesVisitor class_loader_visitor(this);
+ VisitClassLoaders(&class_loader_visitor);
+ VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
+ class_loader = class_loader_visitor.GetClassLoader();
+ DCHECK_EQ(class_loader != nullptr, compile_app_image_);
+ }
+
+ // Clear references to removed classes from the DexCaches.
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches;
+ {
+ ReaderMutexLock mu2(self, *Locks::dex_lock_);
+ dex_caches.reserve(class_linker->GetDexCachesData().size());
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ if (self->IsJWeakCleared(data.weak_root)) {
+ continue;
+ }
+ dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
+ }
+ }
+ for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
+ PruneAndPreloadDexCache(dex_cache, class_loader);
+ }
+
+ // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
+ class_linker->DropFindArrayClassCache();
+
+ // Clear to save RAM.
+ prune_class_memo_.clear();
+}
+
+void ImageWriter::CheckNonImageClassesRemoved() {
+ if (compiler_driver_.GetImageClasses() != nullptr) {
+ auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj->IsClass() && !IsInBootImage(obj)) {
+ Class* klass = obj->AsClass();
+ if (!KeepClass(klass)) {
+ DumpImageClasses();
+ std::string temp;
+ CHECK(KeepClass(klass))
+ << Runtime::Current()->GetHeap()->GetVerification()->FirstPathFromRootSet(klass);
+ }
+ }
+ };
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ heap->VisitObjects(visitor);
+ }
+}
+
+void ImageWriter::DumpImageClasses() {
+ auto image_classes = compiler_driver_.GetImageClasses();
+ CHECK(image_classes != nullptr);
+ for (const std::string& image_class : *image_classes) {
+ LOG(INFO) << " " << image_class;
+ }
+}
+
+mirror::String* ImageWriter::FindInternedString(mirror::String* string) {
+ Thread* const self = Thread::Current();
+ for (const ImageInfo& image_info : image_infos_) {
+ ObjPtr<mirror::String> const found = image_info.intern_table_->LookupStrong(self, string);
+ DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr)
+ << string->ToModifiedUtf8();
+ if (found != nullptr) {
+ return found.Ptr();
+ }
+ }
+ if (compile_app_image_) {
+ Runtime* const runtime = Runtime::Current();
+ ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string);
+ // If we found it in the runtime intern table it could either be in the boot image or interned
+ // during app image compilation. If it was in the boot image return that, otherwise return null
+ // since it belongs to another image space.
+ if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found.Ptr())) {
+ return found.Ptr();
+ }
+ DCHECK(runtime->GetInternTable()->LookupWeak(self, string) == nullptr)
+ << string->ToModifiedUtf8();
+ }
+ return nullptr;
+}
+
+
+ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ Thread* self = Thread::Current();
+ StackHandleScope<3> hs(self);
+ Handle<Class> object_array_class(hs.NewHandle(
+ class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
+
+ std::unordered_set<const DexFile*> image_dex_files;
+ for (auto& pair : dex_file_oat_index_map_) {
+ const DexFile* image_dex_file = pair.first;
+ size_t image_oat_index = pair.second;
+ if (oat_index == image_oat_index) {
+ image_dex_files.insert(image_dex_file);
+ }
+ }
+
+ // build an Object[] of all the DexCaches used in the source_space_.
+ // Since we can't hold the dex lock when allocating the dex_caches
+ // ObjectArray, we lock the dex lock twice, first to get the number
+ // of dex caches first and then lock it again to copy the dex
+ // caches. We check that the number of dex caches does not change.
+ size_t dex_cache_count = 0;
+ {
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ // Count number of dex caches not in the boot image.
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ ObjPtr<mirror::DexCache> dex_cache =
+ ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
+ if (dex_cache == nullptr) {
+ continue;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (!IsInBootImage(dex_cache.Ptr())) {
+ dex_cache_count += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
+ }
+ }
+ }
+ Handle<ObjectArray<Object>> dex_caches(
+ hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count)));
+ CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
+ {
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ size_t non_image_dex_caches = 0;
+ // Re-count number of non image dex caches.
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ ObjPtr<mirror::DexCache> dex_cache =
+ ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
+ if (dex_cache == nullptr) {
+ continue;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (!IsInBootImage(dex_cache.Ptr())) {
+ non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
+ }
+ }
+ CHECK_EQ(dex_cache_count, non_image_dex_caches)
+ << "The number of non-image dex caches changed.";
+ size_t i = 0;
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ ObjPtr<mirror::DexCache> dex_cache =
+ ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
+ if (dex_cache == nullptr) {
+ continue;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (!IsInBootImage(dex_cache.Ptr()) &&
+ image_dex_files.find(dex_file) != image_dex_files.end()) {
+ dex_caches->Set<false>(i, dex_cache.Ptr());
+ ++i;
+ }
+ }
+ }
+
+ // build an Object[] of the roots needed to restore the runtime
+ int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
+ auto image_roots(hs.NewHandle(
+ ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
+ image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
+ image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
+ // image_roots[ImageHeader::kClassLoader] will be set later for app image.
+ static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+ "Class loader should be the last image root.");
+ for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
+ CHECK(image_roots->Get(i) != nullptr);
+ }
+ return image_roots.Get();
+}
+
+mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
+ mirror::Object* obj,
+ size_t oat_index) {
+ if (obj == nullptr || IsInBootImage(obj)) {
+ // Object is null or already in the image, there is no work to do.
+ return obj;
+ }
+ if (!IsImageBinSlotAssigned(obj)) {
+ // We want to intern all strings but also assign offsets for the source string. Since the
+ // pruning phase has already happened, if we intern a string to one in the image we still
+ // end up copying an unreachable string.
+ if (obj->IsString()) {
+ // Need to check if the string is already interned in another image info so that we don't have
+ // the intern tables of two different images contain the same string.
+ mirror::String* interned = FindInternedString(obj->AsString());
+ if (interned == nullptr) {
+ // Not in another image space, insert to our table.
+ interned =
+ GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString()).Ptr();
+ DCHECK_EQ(interned, obj);
+ }
+ } else if (obj->IsDexCache()) {
+ oat_index = GetOatIndexForDexCache(obj->AsDexCache());
+ } else if (obj->IsClass()) {
+ // Visit and assign offsets for fields and field arrays.
+ mirror::Class* as_klass = obj->AsClass();
+ mirror::DexCache* dex_cache = as_klass->GetDexCache();
+ DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
+ if (compile_app_image_) {
+ // Extra sanity, no boot loader classes should be left!
+ CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
+ }
+ LengthPrefixedArray<ArtField>* fields[] = {
+ as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(),
+ };
+ // Overwrite the oat index value since the class' dex cache is more accurate of where it
+ // belongs.
+ oat_index = GetOatIndexForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ if (!compile_app_image_) {
+ // Note: Avoid locking to prevent lock order violations from root visiting;
+ // image_info.class_table_ is only accessed from the image writer.
+ image_info.class_table_->InsertWithoutLocks(as_klass);
+ }
+ for (LengthPrefixedArray<ArtField>* cur_fields : fields) {
+ // Total array length including header.
+ if (cur_fields != nullptr) {
+ const size_t header_size = LengthPrefixedArray<ArtField>::ComputeSize(0);
+ // Forward the entire array at once.
+ auto it = native_object_relocations_.find(cur_fields);
+ CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields
+ << " already forwarded";
+ size_t& offset = image_info.bin_slot_sizes_[kBinArtField];
+ DCHECK(!IsInBootImage(cur_fields));
+ native_object_relocations_.emplace(
+ cur_fields,
+ NativeObjectRelocation {
+ oat_index, offset, kNativeObjectRelocationTypeArtFieldArray
+ });
+ offset += header_size;
+ // Forward individual fields so that we can quickly find where they belong.
+ for (size_t i = 0, count = cur_fields->size(); i < count; ++i) {
+ // Need to forward arrays separate of fields.
+ ArtField* field = &cur_fields->At(i);
+ auto it2 = native_object_relocations_.find(field);
+ CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i
+ << " already assigned " << field->PrettyField() << " static=" << field->IsStatic();
+ DCHECK(!IsInBootImage(field));
+ native_object_relocations_.emplace(
+ field,
+ NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField });
+ offset += sizeof(ArtField);
+ }
+ }
+ }
+ // Visit and assign offsets for methods.
+ size_t num_methods = as_klass->NumMethods();
+ if (num_methods != 0) {
+ bool any_dirty = false;
+ for (auto& m : as_klass->GetMethods(target_ptr_size_)) {
+ if (WillMethodBeDirty(&m)) {
+ any_dirty = true;
+ break;
+ }
+ }
+ NativeObjectRelocationType type = any_dirty
+ ? kNativeObjectRelocationTypeArtMethodDirty
+ : kNativeObjectRelocationTypeArtMethodClean;
+ Bin bin_type = BinTypeForNativeRelocationType(type);
+ // Forward the entire array at once, but header first.
+ const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
+ const size_t method_size = ArtMethod::Size(target_ptr_size_);
+ const size_t header_size = LengthPrefixedArray<ArtMethod>::ComputeSize(0,
+ method_size,
+ method_alignment);
+ LengthPrefixedArray<ArtMethod>* array = as_klass->GetMethodsPtr();
+ auto it = native_object_relocations_.find(array);
+ CHECK(it == native_object_relocations_.end())
+ << "Method array " << array << " already forwarded";
+ size_t& offset = image_info.bin_slot_sizes_[bin_type];
+ DCHECK(!IsInBootImage(array));
+ native_object_relocations_.emplace(array,
+ NativeObjectRelocation {
+ oat_index,
+ offset,
+ any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty
+ : kNativeObjectRelocationTypeArtMethodArrayClean });
+ offset += header_size;
+ for (auto& m : as_klass->GetMethods(target_ptr_size_)) {
+ AssignMethodOffset(&m, type, oat_index);
+ }
+ (any_dirty ? dirty_methods_ : clean_methods_) += num_methods;
+ }
+ // Assign offsets for all runtime methods in the IMT since these may hold conflict tables
+ // live.
+ if (as_klass->ShouldHaveImt()) {
+ ImTable* imt = as_klass->GetImt(target_ptr_size_);
+ if (TryAssignImTableOffset(imt, oat_index)) {
+ // Since imt's can be shared only do this the first time to not double count imt method
+ // fixups.
+ for (size_t i = 0; i < ImTable::kSize; ++i) {
+ ArtMethod* imt_method = imt->Get(i, target_ptr_size_);
+ DCHECK(imt_method != nullptr);
+ if (imt_method->IsRuntimeMethod() &&
+ !IsInBootImage(imt_method) &&
+ !NativeRelocationAssigned(imt_method)) {
+ AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index);
+ }
+ }
+ }
+ }
+ } else if (obj->IsClassLoader()) {
+ // Register the class loader if it has a class table.
+ // The fake boot class loader should not get registered and we should end up with only one
+ // class loader.
+ mirror::ClassLoader* class_loader = obj->AsClassLoader();
+ if (class_loader->GetClassTable() != nullptr) {
+ DCHECK(compile_app_image_);
+ DCHECK(class_loaders_.empty());
+ class_loaders_.insert(class_loader);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ // Note: Avoid locking to prevent lock order violations from root visiting;
+ // image_info.class_table_ table is only accessed from the image writer
+ // and class_loader->GetClassTable() is iterated but not modified.
+ image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable());
+ }
+ }
+ AssignImageBinSlot(obj, oat_index);
+ work_stack.emplace(obj, oat_index);
+ }
+ if (obj->IsString()) {
+ // Always return the interned string if there exists one.
+ mirror::String* interned = FindInternedString(obj->AsString());
+ if (interned != nullptr) {
+ return interned;
+ }
+ }
+ return obj;
+}
+
+bool ImageWriter::NativeRelocationAssigned(void* ptr) const {
+ return native_object_relocations_.find(ptr) != native_object_relocations_.end();
+}
+
+bool ImageWriter::TryAssignImTableOffset(ImTable* imt, size_t oat_index) {
+ // No offset, or already assigned.
+ if (imt == nullptr || IsInBootImage(imt) || NativeRelocationAssigned(imt)) {
+ return false;
+ }
+ // If the method is a conflict method we also want to assign the conflict table offset.
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ const size_t size = ImTable::SizeInBytes(target_ptr_size_);
+ native_object_relocations_.emplace(
+ imt,
+ NativeObjectRelocation {
+ oat_index,
+ image_info.bin_slot_sizes_[kBinImTable],
+ kNativeObjectRelocationTypeIMTable});
+ image_info.bin_slot_sizes_[kBinImTable] += size;
+ return true;
+}
+
+void ImageWriter::TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index) {
+ // No offset, or already assigned.
+ if (table == nullptr || NativeRelocationAssigned(table)) {
+ return;
+ }
+ CHECK(!IsInBootImage(table));
+ // If the method is a conflict method we also want to assign the conflict table offset.
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ const size_t size = table->ComputeSize(target_ptr_size_);
+ native_object_relocations_.emplace(
+ table,
+ NativeObjectRelocation {
+ oat_index,
+ image_info.bin_slot_sizes_[kBinIMTConflictTable],
+ kNativeObjectRelocationTypeIMTConflictTable});
+ image_info.bin_slot_sizes_[kBinIMTConflictTable] += size;
+}
+
+void ImageWriter::AssignMethodOffset(ArtMethod* method,
+ NativeObjectRelocationType type,
+ size_t oat_index) {
+ DCHECK(!IsInBootImage(method));
+ CHECK(!NativeRelocationAssigned(method)) << "Method " << method << " already assigned "
+ << ArtMethod::PrettyMethod(method);
+ if (method->IsRuntimeMethod()) {
+ TryAssignConflictTableOffset(method->GetImtConflictTable(target_ptr_size_), oat_index);
+ }
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)];
+ native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type });
+ offset += ArtMethod::Size(target_ptr_size_);
+}
+
+void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) {
+ DCHECK(!IsInBootImage(obj));
+ CHECK(obj != nullptr);
+
+ // We know the bin slot, and the total bin sizes for all objects by now,
+ // so calculate the object's final image offset.
+
+ DCHECK(IsImageBinSlotAssigned(obj));
+ BinSlot bin_slot = GetImageBinSlot(obj);
+ // Change the lockword from a bin slot into an offset
+ AssignImageOffset(obj, bin_slot);
+}
+
+class ImageWriter::VisitReferencesVisitor {
+ public:
+ VisitReferencesVisitor(ImageWriter* image_writer, WorkStack* work_stack, size_t oat_index)
+ : image_writer_(image_writer), work_stack_(work_stack), oat_index_(oat_index) {}
+
+ // Fix up separately since we also need to fix up method entrypoints.
+ ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ root->Assign(VisitReference(root->AsMirrorPtr()));
+ }
+
+ ALWAYS_INLINE void operator() (ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Object* ref =
+ obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
+ obj->SetFieldObject</*kTransactionActive*/false>(offset, VisitReference(ref));
+ }
+
+ ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
+ }
+
+ private:
+ mirror::Object* VisitReference(mirror::Object* ref) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return image_writer_->TryAssignBinSlot(*work_stack_, ref, oat_index_);
+ }
+
+ ImageWriter* const image_writer_;
+ WorkStack* const work_stack_;
+ const size_t oat_index_;
+};
+
+class ImageWriter::GetRootsVisitor : public RootVisitor {
+ public:
+ explicit GetRootsVisitor(std::vector<mirror::Object*>* roots) : roots_(roots) {}
+
+ void VisitRoots(mirror::Object*** roots,
+ size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ roots_->push_back(*roots[i]);
+ }
+ }
+
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots,
+ size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ roots_->push_back(roots[i]->AsMirrorPtr());
+ }
+ }
+
+ private:
+ std::vector<mirror::Object*>* const roots_;
+};
+
+void ImageWriter::ProcessWorkStack(WorkStack* work_stack) {
+ while (!work_stack->empty()) {
+ std::pair<mirror::Object*, size_t> pair(work_stack->top());
+ work_stack->pop();
+ VisitReferencesVisitor visitor(this, work_stack, /*oat_index*/ pair.second);
+ // Walk references and assign bin slots for them.
+ pair.first->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
+ visitor,
+ visitor);
+ }
+}
+
+void ImageWriter::CalculateNewObjectOffsets() {
+ Thread* const self = Thread::Current();
+ VariableSizedHandleScope handles(self);
+ std::vector<Handle<ObjectArray<Object>>> image_roots;
+ for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
+ image_roots.push_back(handles.NewHandle(CreateImageRoots(i)));
+ }
+
+ Runtime* const runtime = Runtime::Current();
+ gc::Heap* const heap = runtime->GetHeap();
+
+ // Leave space for the header, but do not write it yet, we need to
+ // know where image_roots is going to end up
+ image_objects_offset_begin_ = RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
+
+ const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
+ // Write the image runtime methods.
+ image_methods_[ImageHeader::kResolutionMethod] = runtime->GetResolutionMethod();
+ image_methods_[ImageHeader::kImtConflictMethod] = runtime->GetImtConflictMethod();
+ image_methods_[ImageHeader::kImtUnimplementedMethod] = runtime->GetImtUnimplementedMethod();
+ image_methods_[ImageHeader::kSaveAllCalleeSavesMethod] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves);
+ image_methods_[ImageHeader::kSaveRefsOnlyMethod] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsOnly);
+ image_methods_[ImageHeader::kSaveRefsAndArgsMethod] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs);
+ image_methods_[ImageHeader::kSaveEverythingMethod] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything);
+ image_methods_[ImageHeader::kSaveEverythingMethodForClinit] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit);
+ image_methods_[ImageHeader::kSaveEverythingMethodForSuspendCheck] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck);
+ // Visit image methods first to have the main runtime methods in the first image.
+ for (auto* m : image_methods_) {
+ CHECK(m != nullptr);
+ CHECK(m->IsRuntimeMethod());
+ DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
+ if (!IsInBootImage(m)) {
+ AssignMethodOffset(m, kNativeObjectRelocationTypeRuntimeMethod, GetDefaultOatIndex());
+ }
+ }
+
+ // Deflate monitors before we visit roots since deflating acquires the monitor lock. Acquiring
+ // this lock while holding other locks may cause lock order violations.
+ {
+ auto deflate_monitor = [](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ Monitor::Deflate(Thread::Current(), obj);
+ };
+ heap->VisitObjects(deflate_monitor);
+ }
+
+ // Work list of <object, oat_index> for objects. Everything on the stack must already be
+ // assigned a bin slot.
+ WorkStack work_stack;
+
+ // Special case interned strings to put them in the image they are likely to be resolved from.
+ for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) {
+ auto it = dex_file_oat_index_map_.find(dex_file);
+ DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
+ const size_t oat_index = it->second;
+ InternTable* const intern_table = runtime->GetInternTable();
+ for (size_t i = 0, count = dex_file->NumStringIds(); i < count; ++i) {
+ uint32_t utf16_length;
+ const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i),
+ &utf16_length);
+ mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data).Ptr();
+ TryAssignBinSlot(work_stack, string, oat_index);
+ }
+ }
+
+ // Get the GC roots and then visit them separately to avoid lock violations since the root visitor
+ // visits roots while holding various locks.
+ {
+ std::vector<mirror::Object*> roots;
+ GetRootsVisitor root_visitor(&roots);
+ runtime->VisitRoots(&root_visitor);
+ for (mirror::Object* obj : roots) {
+ TryAssignBinSlot(work_stack, obj, GetDefaultOatIndex());
+ }
+ }
+ ProcessWorkStack(&work_stack);
+
+ // For app images, there may be objects that are only held live by the by the boot image. One
+ // example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback
+ // does not fail any checks. TODO: We should probably avoid copying these objects.
+ if (compile_app_image_) {
+ for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) {
+ DCHECK(space->IsImageSpace());
+ gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+ reinterpret_cast<uintptr_t>(space->Limit()),
+ [this, &work_stack](mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ VisitReferencesVisitor visitor(this, &work_stack, GetDefaultOatIndex());
+ // Visit all references and try to assign bin slots for them (calls TryAssignBinSlot).
+ obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
+ visitor,
+ visitor);
+ });
+ }
+ // Process the work stack in case anything was added by TryAssignBinSlot.
+ ProcessWorkStack(&work_stack);
+
+ // Store the class loader in the class roots.
+ CHECK_EQ(class_loaders_.size(), 1u);
+ CHECK_EQ(image_roots.size(), 1u);
+ CHECK(*class_loaders_.begin() != nullptr);
+ image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
+ }
+
+ // Verify that all objects have assigned image bin slots.
+ {
+ auto ensure_bin_slots_assigned = [&](mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(obj)) {
+ CHECK(IsImageBinSlotAssigned(obj)) << mirror::Object::PrettyTypeOf(obj) << " " << obj;
+ }
+ };
+ heap->VisitObjects(ensure_bin_slots_assigned);
+ }
+
+ // Calculate size of the dex cache arrays slot and prepare offsets.
+ PrepareDexCacheArraySlots();
+
+ // Calculate the sizes of the intern tables, class tables, and fixup tables.
+ for (ImageInfo& image_info : image_infos_) {
+ // Calculate how big the intern table will be after being serialized.
+ InternTable* const intern_table = image_info.intern_table_.get();
+ CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings";
+ if (intern_table->StrongSize() != 0u) {
+ image_info.intern_table_bytes_ = intern_table->WriteToMemory(nullptr);
+ }
+
+ // Calculate the size of the class table.
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ DCHECK_EQ(image_info.class_table_->NumReferencedZygoteClasses(), 0u);
+ if (image_info.class_table_->NumReferencedNonZygoteClasses() != 0u) {
+ image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr);
+ }
+ }
+
+ // Calculate bin slot offsets.
+ for (ImageInfo& image_info : image_infos_) {
+ size_t bin_offset = image_objects_offset_begin_;
+ for (size_t i = 0; i != kBinSize; ++i) {
+ switch (i) {
+ case kBinArtMethodClean:
+ case kBinArtMethodDirty: {
+ bin_offset = RoundUp(bin_offset, method_alignment);
+ break;
+ }
+ case kBinDexCacheArray:
+ bin_offset = RoundUp(bin_offset, DexCacheArraysLayout::Alignment(target_ptr_size_));
+ break;
+ case kBinImTable:
+ case kBinIMTConflictTable: {
+ bin_offset = RoundUp(bin_offset, static_cast<size_t>(target_ptr_size_));
+ break;
+ }
+ default: {
+ // Normal alignment.
+ }
+ }
+ image_info.bin_slot_offsets_[i] = bin_offset;
+ bin_offset += image_info.bin_slot_sizes_[i];
+ }
+ // NOTE: There may be additional padding between the bin slots and the intern table.
+ DCHECK_EQ(image_info.image_end_,
+ GetBinSizeSum(image_info, kBinMirrorCount) + image_objects_offset_begin_);
+ }
+
+ // Calculate image offsets.
+ size_t image_offset = 0;
+ for (ImageInfo& image_info : image_infos_) {
+ image_info.image_begin_ = global_image_begin_ + image_offset;
+ image_info.image_offset_ = image_offset;
+ ImageSection unused_sections[ImageHeader::kSectionCount];
+ image_info.image_size_ =
+ RoundUp(image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize);
+ // There should be no gaps until the next image.
+ image_offset += image_info.image_size_;
+ }
+
+ // Transform each object's bin slot into an offset which will be used to do the final copy.
+ {
+ auto unbin_objects_into_offset = [&](mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!IsInBootImage(obj)) {
+ UnbinObjectsIntoOffset(obj);
+ }
+ };
+ heap->VisitObjects(unbin_objects_into_offset);
+ }
+
+ size_t i = 0;
+ for (ImageInfo& image_info : image_infos_) {
+ image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get()));
+ i++;
+ }
+
+ // Update the native relocations by adding their bin sums.
+ for (auto& pair : native_object_relocations_) {
+ NativeObjectRelocation& relocation = pair.second;
+ Bin bin_type = BinTypeForNativeRelocationType(relocation.type);
+ ImageInfo& image_info = GetImageInfo(relocation.oat_index);
+ relocation.offset += image_info.bin_slot_offsets_[bin_type];
+ }
+}
+
+size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections,
+ bool app_image) const {
+ DCHECK(out_sections != nullptr);
+
+ // Do not round up any sections here that are represented by the bins since it will break
+ // offsets.
+
+ // Objects section
+ ImageSection* objects_section = &out_sections[ImageHeader::kSectionObjects];
+ *objects_section = ImageSection(0u, image_end_);
+
+ // Add field section.
+ ImageSection* field_section = &out_sections[ImageHeader::kSectionArtFields];
+ *field_section = ImageSection(bin_slot_offsets_[kBinArtField], bin_slot_sizes_[kBinArtField]);
+ CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset());
+
+ // Add method section.
+ ImageSection* methods_section = &out_sections[ImageHeader::kSectionArtMethods];
+ *methods_section = ImageSection(
+ bin_slot_offsets_[kBinArtMethodClean],
+ bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinArtMethodDirty]);
+
+ // IMT section.
+ ImageSection* imt_section = &out_sections[ImageHeader::kSectionImTables];
+ *imt_section = ImageSection(bin_slot_offsets_[kBinImTable], bin_slot_sizes_[kBinImTable]);
+
+ // Conflict tables section.
+ ImageSection* imt_conflict_tables_section = &out_sections[ImageHeader::kSectionIMTConflictTables];
+ *imt_conflict_tables_section = ImageSection(bin_slot_offsets_[kBinIMTConflictTable],
+ bin_slot_sizes_[kBinIMTConflictTable]);
+
+ // Runtime methods section.
+ ImageSection* runtime_methods_section = &out_sections[ImageHeader::kSectionRuntimeMethods];
+ *runtime_methods_section = ImageSection(bin_slot_offsets_[kBinRuntimeMethod],
+ bin_slot_sizes_[kBinRuntimeMethod]);
+
+ // Add dex cache arrays section.
+ ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays];
+ *dex_cache_arrays_section = ImageSection(bin_slot_offsets_[kBinDexCacheArray],
+ bin_slot_sizes_[kBinDexCacheArray]);
+ // For boot image, round up to the page boundary to separate the interned strings and
+ // class table from the modifiable data. We shall mprotect() these pages read-only when
+ // we load the boot image. This is more than sufficient for the string table alignment,
+ // namely sizeof(uint64_t). See HashSet::WriteToMemory.
+ static_assert(IsAligned<sizeof(uint64_t)>(kPageSize), "String table alignment check.");
+ size_t cur_pos =
+ RoundUp(dex_cache_arrays_section->End(), app_image ? sizeof(uint64_t) : kPageSize);
+ // Calculate the size of the interned strings.
+ ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings];
+ *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_);
+ cur_pos = interned_strings_section->End();
+ // Round up to the alignment the class table expects. See HashSet::WriteToMemory.
+ cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
+ // Calculate the size of the class table section.
+ ImageSection* class_table_section = &out_sections[ImageHeader::kSectionClassTable];
+ *class_table_section = ImageSection(cur_pos, class_table_bytes_);
+ cur_pos = class_table_section->End();
+ // Image end goes right before the start of the image bitmap.
+ return cur_pos;
+}
+
+void ImageWriter::CreateHeader(size_t oat_index) {
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ const uint8_t* oat_file_begin = image_info.oat_file_begin_;
+ const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_;
+ const uint8_t* oat_data_end = image_info.oat_data_begin_ + image_info.oat_size_;
+
+ // Create the image sections.
+ ImageSection sections[ImageHeader::kSectionCount];
+ const size_t image_end = image_info.CreateImageSections(sections, compile_app_image_);
+
+ // Finally bitmap section.
+ const size_t bitmap_bytes = image_info.image_bitmap_->Size();
+ auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap];
+ *bitmap_section = ImageSection(RoundUp(image_end, kPageSize), RoundUp(bitmap_bytes, kPageSize));
+ if (VLOG_IS_ON(compiler)) {
+ LOG(INFO) << "Creating header for " << oat_filenames_[oat_index];
+ size_t idx = 0;
+ for (const ImageSection& section : sections) {
+ LOG(INFO) << static_cast<ImageHeader::ImageSections>(idx) << " " << section;
+ ++idx;
+ }
+ LOG(INFO) << "Methods: clean=" << clean_methods_ << " dirty=" << dirty_methods_;
+ LOG(INFO) << "Image roots address=" << std::hex << image_info.image_roots_address_ << std::dec;
+ LOG(INFO) << "Image begin=" << std::hex << reinterpret_cast<uintptr_t>(global_image_begin_)
+ << " Image offset=" << image_info.image_offset_ << std::dec;
+ LOG(INFO) << "Oat file begin=" << std::hex << reinterpret_cast<uintptr_t>(oat_file_begin)
+ << " Oat data begin=" << reinterpret_cast<uintptr_t>(image_info.oat_data_begin_)
+ << " Oat data end=" << reinterpret_cast<uintptr_t>(oat_data_end)
+ << " Oat file end=" << reinterpret_cast<uintptr_t>(oat_file_end);
+ }
+ // Store boot image info for app image so that we can relocate.
+ uint32_t boot_image_begin = 0;
+ uint32_t boot_image_end = 0;
+ uint32_t boot_oat_begin = 0;
+ uint32_t boot_oat_end = 0;
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end);
+
+ // Create the header, leave 0 for data size since we will fill this in as we are writing the
+ // image.
+ new (image_info.image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_info.image_begin_),
+ image_end,
+ sections,
+ image_info.image_roots_address_,
+ image_info.oat_checksum_,
+ PointerToLowMemUInt32(oat_file_begin),
+ PointerToLowMemUInt32(image_info.oat_data_begin_),
+ PointerToLowMemUInt32(oat_data_end),
+ PointerToLowMemUInt32(oat_file_end),
+ boot_image_begin,
+ boot_image_end - boot_image_begin,
+ boot_oat_begin,
+ boot_oat_end - boot_oat_begin,
+ static_cast<uint32_t>(target_ptr_size_),
+ compile_pic_,
+ /*is_pic*/compile_app_image_,
+ image_storage_mode_,
+ /*data_size*/0u);
+}
+
+ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) {
+ auto it = native_object_relocations_.find(method);
+ CHECK(it != native_object_relocations_.end()) << ArtMethod::PrettyMethod(method) << " @ "
+ << method;
+ size_t oat_index = GetOatIndex(method->GetDexCache());
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects";
+ return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset);
+}
+
+class ImageWriter::FixupRootVisitor : public RootVisitor {
+ public:
+ explicit FixupRootVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {
+ }
+
+ void VisitRoots(mirror::Object*** roots ATTRIBUTE_UNUSED,
+ size_t count ATTRIBUTE_UNUSED,
+ const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ LOG(FATAL) << "Unsupported";
+ }
+
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ image_writer_->CopyReference(roots[i], roots[i]->AsMirrorPtr());
+ }
+ }
+
+ private:
+ ImageWriter* const image_writer_;
+};
+
+void ImageWriter::CopyAndFixupImTable(ImTable* orig, ImTable* copy) {
+ for (size_t i = 0; i < ImTable::kSize; ++i) {
+ ArtMethod* method = orig->Get(i, target_ptr_size_);
+ void** address = reinterpret_cast<void**>(copy->AddressOfElement(i, target_ptr_size_));
+ CopyAndFixupPointer(address, method);
+ DCHECK_EQ(copy->Get(i, target_ptr_size_), NativeLocationInImage(method));
+ }
+}
+
+void ImageWriter::CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy) {
+ const size_t count = orig->NumEntries(target_ptr_size_);
+ for (size_t i = 0; i < count; ++i) {
+ ArtMethod* interface_method = orig->GetInterfaceMethod(i, target_ptr_size_);
+ ArtMethod* implementation_method = orig->GetImplementationMethod(i, target_ptr_size_);
+ CopyAndFixupPointer(copy->AddressOfInterfaceMethod(i, target_ptr_size_), interface_method);
+ CopyAndFixupPointer(copy->AddressOfImplementationMethod(i, target_ptr_size_),
+ implementation_method);
+ DCHECK_EQ(copy->GetInterfaceMethod(i, target_ptr_size_),
+ NativeLocationInImage(interface_method));
+ DCHECK_EQ(copy->GetImplementationMethod(i, target_ptr_size_),
+ NativeLocationInImage(implementation_method));
+ }
+}
+
+void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
+ const ImageInfo& image_info = GetImageInfo(oat_index);
+ // Copy ArtFields and methods to their locations and update the array for convenience.
+ for (auto& pair : native_object_relocations_) {
+ NativeObjectRelocation& relocation = pair.second;
+ // Only work with fields and methods that are in the current oat file.
+ if (relocation.oat_index != oat_index) {
+ continue;
+ }
+ auto* dest = image_info.image_->Begin() + relocation.offset;
+ DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_);
+ DCHECK(!IsInBootImage(pair.first));
+ switch (relocation.type) {
+ case kNativeObjectRelocationTypeArtField: {
+ memcpy(dest, pair.first, sizeof(ArtField));
+ CopyReference(
+ reinterpret_cast<ArtField*>(dest)->GetDeclaringClassAddressWithoutBarrier(),
+ reinterpret_cast<ArtField*>(pair.first)->GetDeclaringClass().Ptr());
+ break;
+ }
+ case kNativeObjectRelocationTypeRuntimeMethod:
+ case kNativeObjectRelocationTypeArtMethodClean:
+ case kNativeObjectRelocationTypeArtMethodDirty: {
+ CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first),
+ reinterpret_cast<ArtMethod*>(dest),
+ image_info);
+ break;
+ }
+ // For arrays, copy just the header since the elements will get copied by their corresponding
+ // relocations.
+ case kNativeObjectRelocationTypeArtFieldArray: {
+ memcpy(dest, pair.first, LengthPrefixedArray<ArtField>::ComputeSize(0));
+ break;
+ }
+ case kNativeObjectRelocationTypeArtMethodArrayClean:
+ case kNativeObjectRelocationTypeArtMethodArrayDirty: {
+ size_t size = ArtMethod::Size(target_ptr_size_);
+ size_t alignment = ArtMethod::Alignment(target_ptr_size_);
+ memcpy(dest, pair.first, LengthPrefixedArray<ArtMethod>::ComputeSize(0, size, alignment));
+ // Clear padding to avoid non-deterministic data in the image (and placate valgrind).
+ reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(dest)->ClearPadding(size, alignment);
+ break;
+ }
+ case kNativeObjectRelocationTypeDexCacheArray:
+ // Nothing to copy here, everything is done in FixupDexCache().
+ break;
+ case kNativeObjectRelocationTypeIMTable: {
+ ImTable* orig_imt = reinterpret_cast<ImTable*>(pair.first);
+ ImTable* dest_imt = reinterpret_cast<ImTable*>(dest);
+ CopyAndFixupImTable(orig_imt, dest_imt);
+ break;
+ }
+ case kNativeObjectRelocationTypeIMTConflictTable: {
+ auto* orig_table = reinterpret_cast<ImtConflictTable*>(pair.first);
+ CopyAndFixupImtConflictTable(
+ orig_table,
+ new(dest)ImtConflictTable(orig_table->NumEntries(target_ptr_size_), target_ptr_size_));
+ break;
+ }
+ }
+ }
+ // Fixup the image method roots.
+ auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
+ for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) {
+ ArtMethod* method = image_methods_[i];
+ CHECK(method != nullptr);
+ if (!IsInBootImage(method)) {
+ method = NativeLocationInImage(method);
+ }
+ image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method);
+ }
+ FixupRootVisitor root_visitor(this);
+
+ // Write the intern table into the image.
+ if (image_info.intern_table_bytes_ > 0) {
+ const ImageSection& intern_table_section = image_header->GetInternedStringsSection();
+ InternTable* const intern_table = image_info.intern_table_.get();
+ uint8_t* const intern_table_memory_ptr =
+ image_info.image_->Begin() + intern_table_section.Offset();
+ const size_t intern_table_bytes = intern_table->WriteToMemory(intern_table_memory_ptr);
+ CHECK_EQ(intern_table_bytes, image_info.intern_table_bytes_);
+ // Fixup the pointers in the newly written intern table to contain image addresses.
+ InternTable temp_intern_table;
+ // Note that we require that ReadFromMemory does not make an internal copy of the elements so that
+ // the VisitRoots() will update the memory directly rather than the copies.
+ // This also relies on visit roots not doing any verification which could fail after we update
+ // the roots to be the image addresses.
+ temp_intern_table.AddTableFromMemory(intern_table_memory_ptr);
+ CHECK_EQ(temp_intern_table.Size(), intern_table->Size());
+ temp_intern_table.VisitRoots(&root_visitor, kVisitRootFlagAllRoots);
+ }
+ // Write the class table(s) into the image. class_table_bytes_ may be 0 if there are multiple
+ // class loaders. Writing multiple class tables into the image is currently unsupported.
+ if (image_info.class_table_bytes_ > 0u) {
+ const ImageSection& class_table_section = image_header->GetClassTableSection();
+ uint8_t* const class_table_memory_ptr =
+ image_info.image_->Begin() + class_table_section.Offset();
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+
+ ClassTable* table = image_info.class_table_.get();
+ CHECK(table != nullptr);
+ const size_t class_table_bytes = table->WriteToMemory(class_table_memory_ptr);
+ CHECK_EQ(class_table_bytes, image_info.class_table_bytes_);
+ // Fixup the pointers in the newly written class table to contain image addresses. See
+ // above comment for intern tables.
+ ClassTable temp_class_table;
+ temp_class_table.ReadFromMemory(class_table_memory_ptr);
+ CHECK_EQ(temp_class_table.NumReferencedZygoteClasses(),
+ table->NumReferencedNonZygoteClasses() + table->NumReferencedZygoteClasses());
+ UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown));
+ temp_class_table.VisitRoots(visitor);
+ }
+}
+
+void ImageWriter::CopyAndFixupObjects() {
+ auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(obj != nullptr);
+ CopyAndFixupObject(obj);
+ };
+ Runtime::Current()->GetHeap()->VisitObjects(visitor);
+ // Fix up the object previously had hash codes.
+ for (const auto& hash_pair : saved_hashcode_map_) {
+ Object* obj = hash_pair.first;
+ DCHECK_EQ(obj->GetLockWord<kVerifyNone>(false).ReadBarrierState(), 0U);
+ obj->SetLockWord<kVerifyNone>(LockWord::FromHashCode(hash_pair.second, 0U), false);
+ }
+ saved_hashcode_map_.clear();
+}
+
+void ImageWriter::FixupPointerArray(mirror::Object* dst,
+ mirror::PointerArray* arr,
+ mirror::Class* klass,
+ Bin array_type) {
+ CHECK(klass->IsArrayClass());
+ CHECK(arr->IsIntArray() || arr->IsLongArray()) << klass->PrettyClass() << " " << arr;
+ // Fixup int and long pointers for the ArtMethod or ArtField arrays.
+ const size_t num_elements = arr->GetLength();
+ dst->SetClass(GetImageAddress(arr->GetClass()));
+ auto* dest_array = down_cast<mirror::PointerArray*>(dst);
+ for (size_t i = 0, count = num_elements; i < count; ++i) {
+ void* elem = arr->GetElementPtrSize<void*>(i, target_ptr_size_);
+ if (kIsDebugBuild && elem != nullptr && !IsInBootImage(elem)) {
+ auto it = native_object_relocations_.find(elem);
+ if (UNLIKELY(it == native_object_relocations_.end())) {
+ if (it->second.IsArtMethodRelocation()) {
+ auto* method = reinterpret_cast<ArtMethod*>(elem);
+ LOG(FATAL) << "No relocation entry for ArtMethod " << method->PrettyMethod() << " @ "
+ << method << " idx=" << i << "/" << num_elements << " with declaring class "
+ << Class::PrettyClass(method->GetDeclaringClass());
+ } else {
+ CHECK_EQ(array_type, kBinArtField);
+ auto* field = reinterpret_cast<ArtField*>(elem);
+ LOG(FATAL) << "No relocation entry for ArtField " << field->PrettyField() << " @ "
+ << field << " idx=" << i << "/" << num_elements << " with declaring class "
+ << Class::PrettyClass(field->GetDeclaringClass());
+ }
+ UNREACHABLE();
+ }
+ }
+ CopyAndFixupPointer(dest_array->ElementAddress(i, target_ptr_size_), elem);
+ }
+}
+
+void ImageWriter::CopyAndFixupObject(Object* obj) {
+ if (IsInBootImage(obj)) {
+ return;
+ }
+ size_t offset = GetImageOffset(obj);
+ size_t oat_index = GetOatIndex(obj);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset);
+ DCHECK_LT(offset, image_info.image_end_);
+ const auto* src = reinterpret_cast<const uint8_t*>(obj);
+
+ image_info.image_bitmap_->Set(dst); // Mark the obj as live.
+
+ const size_t n = obj->SizeOf();
+ DCHECK_LE(offset + n, image_info.image_->Size());
+ memcpy(dst, src, n);
+
+ // Write in a hash code of objects which have inflated monitors or a hash code in their monitor
+ // word.
+ const auto it = saved_hashcode_map_.find(obj);
+ dst->SetLockWord(it != saved_hashcode_map_.end() ?
+ LockWord::FromHashCode(it->second, 0u) : LockWord::Default(), false);
+ if (kUseBakerReadBarrier && gc::collector::ConcurrentCopying::kGrayDirtyImmuneObjects) {
+ // Treat all of the objects in the image as marked to avoid unnecessary dirty pages. This is
+ // safe since we mark all of the objects that may reference non immune objects as gray.
+ CHECK(dst->AtomicSetMarkBit(0, 1));
+ }
+ FixupObject(obj, dst);
+}
+
+// Rewrite all the references in the copied object to point to their image address equivalent
+class ImageWriter::FixupVisitor {
+ public:
+ FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) {
+ }
+
+ // Ignore class roots since we don't have a way to map them to the destination. These are handled
+ // with other logic.
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {}
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
+
+ void operator()(ObjPtr<Object> obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ ObjPtr<Object> ref = obj->GetFieldObject<Object, kVerifyNone>(offset);
+ // Copy the reference and record the fixup if necessary.
+ image_writer_->CopyReference(
+ copy_->GetFieldObjectReferenceAddr<kVerifyNone>(offset),
+ ref.Ptr());
+ }
+
+ // java.lang.ref.Reference visitor.
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
+ }
+
+ protected:
+ ImageWriter* const image_writer_;
+ mirror::Object* const copy_;
+};
+
+class ImageWriter::FixupClassVisitor FINAL : public FixupVisitor {
+ public:
+ FixupClassVisitor(ImageWriter* image_writer, Object* copy) : FixupVisitor(image_writer, copy) {
+ }
+
+ void operator()(ObjPtr<Object> obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ DCHECK(obj->IsClass());
+ FixupVisitor::operator()(obj, offset, /*is_static*/false);
+ }
+
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ LOG(FATAL) << "Reference not expected here.";
+ }
+};
+
+uintptr_t ImageWriter::NativeOffsetInImage(void* obj) {
+ DCHECK(obj != nullptr);
+ DCHECK(!IsInBootImage(obj));
+ auto it = native_object_relocations_.find(obj);
+ CHECK(it != native_object_relocations_.end()) << obj << " spaces "
+ << Runtime::Current()->GetHeap()->DumpSpaces();
+ const NativeObjectRelocation& relocation = it->second;
+ return relocation.offset;
+}
+
+template <typename T>
+std::string PrettyPrint(T* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream oss;
+ oss << ptr;
+ return oss.str();
+}
+
+template <>
+std::string PrettyPrint(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ArtMethod::PrettyMethod(method);
+}
+
+template <typename T>
+T* ImageWriter::NativeLocationInImage(T* obj) {
+ if (obj == nullptr || IsInBootImage(obj)) {
+ return obj;
+ } else {
+ auto it = native_object_relocations_.find(obj);
+ CHECK(it != native_object_relocations_.end()) << obj << " " << PrettyPrint(obj)
+ << " spaces " << Runtime::Current()->GetHeap()->DumpSpaces();
+ const NativeObjectRelocation& relocation = it->second;
+ ImageInfo& image_info = GetImageInfo(relocation.oat_index);
+ return reinterpret_cast<T*>(image_info.image_begin_ + relocation.offset);
+ }
+}
+
+template <typename T>
+T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) {
+ if (obj == nullptr || IsInBootImage(obj)) {
+ return obj;
+ } else {
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ return reinterpret_cast<T*>(image_info.image_->Begin() + NativeOffsetInImage(obj));
+ }
+}
+
+class ImageWriter::NativeLocationVisitor {
+ public:
+ explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
+
+ template <typename T>
+ T* operator()(T* ptr, void** dest_addr = nullptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (dest_addr != nullptr) {
+ image_writer_->CopyAndFixupPointer(dest_addr, ptr);
+ }
+ return image_writer_->NativeLocationInImage(ptr);
+ }
+
+ private:
+ ImageWriter* const image_writer_;
+};
+
+void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
+ orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this));
+ FixupClassVisitor visitor(this, copy);
+ ObjPtr<mirror::Object>(orig)->VisitReferences(visitor, visitor);
+
+ // Remove the clinitThreadId. This is required for image determinism.
+ copy->SetClinitThreadId(static_cast<pid_t>(0));
+}
+
+void ImageWriter::FixupObject(Object* orig, Object* copy) {
+ DCHECK(orig != nullptr);
+ DCHECK(copy != nullptr);
+ if (kUseBakerReadBarrier) {
+ orig->AssertReadBarrierState();
+ }
+ auto* klass = orig->GetClass();
+ if (klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
+ // Is this a native pointer array?
+ auto it = pointer_arrays_.find(down_cast<mirror::PointerArray*>(orig));
+ if (it != pointer_arrays_.end()) {
+ // Should only need to fixup every pointer array exactly once.
+ FixupPointerArray(copy, down_cast<mirror::PointerArray*>(orig), klass, it->second);
+ pointer_arrays_.erase(it);
+ return;
+ }
+ }
+ if (orig->IsClass()) {
+ FixupClass(orig->AsClass<kVerifyNone>(), down_cast<mirror::Class*>(copy));
+ } else {
+ if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) {
+ // Need to go update the ArtMethod.
+ auto* dest = down_cast<mirror::Executable*>(copy);
+ auto* src = down_cast<mirror::Executable*>(orig);
+ ArtMethod* src_method = src->GetArtMethod();
+ dest->SetArtMethod(GetImageMethodAddress(src_method));
+ } else if (!klass->IsArrayClass()) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) {
+ FixupDexCache(down_cast<mirror::DexCache*>(orig), down_cast<mirror::DexCache*>(copy));
+ } else if (klass->IsClassLoaderClass()) {
+ mirror::ClassLoader* copy_loader = down_cast<mirror::ClassLoader*>(copy);
+ // If src is a ClassLoader, set the class table to null so that it gets recreated by the
+ // ClassLoader.
+ copy_loader->SetClassTable(nullptr);
+ // Also set allocator to null to be safe. The allocator is created when we create the class
+ // table. We also never expect to unload things in the image since they are held live as
+ // roots.
+ copy_loader->SetAllocator(nullptr);
+ }
+ }
+ FixupVisitor visitor(this, copy);
+ orig->VisitReferences(visitor, visitor);
+ }
+}
+
+class ImageWriter::ImageAddressVisitorForDexCacheArray {
+ public:
+ explicit ImageAddressVisitorForDexCacheArray(ImageWriter* image_writer)
+ : image_writer_(image_writer) {}
+
+ template <typename T>
+ T* operator()(T* ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return image_writer_->GetImageAddress(ptr);
+ }
+
+ private:
+ ImageWriter* const image_writer_;
+};
+
+void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
+ mirror::DexCache* copy_dex_cache) {
+ ImageAddressVisitorForDexCacheArray fixup_visitor(this);
+ // Though the DexCache array fields are usually treated as native pointers, we set the full
+ // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
+ // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
+ // static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
+ mirror::StringDexCacheType* orig_strings = orig_dex_cache->GetStrings();
+ if (orig_strings != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(),
+ NativeLocationInImage(orig_strings),
+ PointerSize::k64);
+ orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache), fixup_visitor);
+ }
+ mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
+ if (orig_types != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
+ NativeLocationInImage(orig_types),
+ PointerSize::k64);
+ orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache),
+ fixup_visitor);
+ }
+ mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods();
+ if (orig_methods != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(),
+ NativeLocationInImage(orig_methods),
+ PointerSize::k64);
+ mirror::MethodDexCacheType* copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
+ for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
+ mirror::MethodDexCachePair orig_pair =
+ mirror::DexCache::GetNativePairPtrSize(orig_methods, i, target_ptr_size_);
+ // NativeLocationInImage also handles runtime methods since these have relocation info.
+ mirror::MethodDexCachePair copy_pair(NativeLocationInImage(orig_pair.object),
+ orig_pair.index);
+ mirror::DexCache::SetNativePairPtrSize(copy_methods, i, copy_pair, target_ptr_size_);
+ }
+ }
+ mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields();
+ if (orig_fields != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(),
+ NativeLocationInImage(orig_fields),
+ PointerSize::k64);
+ mirror::FieldDexCacheType* copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
+ for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
+ mirror::FieldDexCachePair orig =
+ mirror::DexCache::GetNativePairPtrSize(orig_fields, i, target_ptr_size_);
+ mirror::FieldDexCachePair copy = orig;
+ copy.object = NativeLocationInImage(orig.object);
+ mirror::DexCache::SetNativePairPtrSize(copy_fields, i, copy, target_ptr_size_);
+ }
+ }
+ mirror::MethodTypeDexCacheType* orig_method_types = orig_dex_cache->GetResolvedMethodTypes();
+ if (orig_method_types != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodTypesOffset(),
+ NativeLocationInImage(orig_method_types),
+ PointerSize::k64);
+ orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache),
+ fixup_visitor);
+ }
+ GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+ if (orig_call_sites != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(),
+ NativeLocationInImage(orig_call_sites),
+ PointerSize::k64);
+ orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache),
+ fixup_visitor);
+ }
+
+ // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
+ // compiler pointers in here will make the output non-deterministic.
+ copy_dex_cache->SetDexFile(nullptr);
+}
+
+const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const {
+ DCHECK_LT(type, kOatAddressCount);
+ // If we are compiling an app image, we need to use the stubs of the boot image.
+ if (compile_app_image_) {
+ // Use the current image pointers.
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ DCHECK(!image_spaces.empty());
+ const OatFile* oat_file = image_spaces[0]->GetOatFile();
+ CHECK(oat_file != nullptr);
+ const OatHeader& header = oat_file->GetOatHeader();
+ switch (type) {
+ // TODO: We could maybe clean this up if we stored them in an array in the oat header.
+ case kOatAddressQuickGenericJNITrampoline:
+ return static_cast<const uint8_t*>(header.GetQuickGenericJniTrampoline());
+ case kOatAddressInterpreterToInterpreterBridge:
+ return static_cast<const uint8_t*>(header.GetInterpreterToInterpreterBridge());
+ case kOatAddressInterpreterToCompiledCodeBridge:
+ return static_cast<const uint8_t*>(header.GetInterpreterToCompiledCodeBridge());
+ case kOatAddressJNIDlsymLookup:
+ return static_cast<const uint8_t*>(header.GetJniDlsymLookup());
+ case kOatAddressQuickIMTConflictTrampoline:
+ return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline());
+ case kOatAddressQuickResolutionTrampoline:
+ return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline());
+ case kOatAddressQuickToInterpreterBridge:
+ return static_cast<const uint8_t*>(header.GetQuickToInterpreterBridge());
+ default:
+ UNREACHABLE();
+ }
+ }
+ const ImageInfo& primary_image_info = GetImageInfo(0);
+ return GetOatAddressForOffset(primary_image_info.oat_address_offsets_[type], primary_image_info);
+}
+
+const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method,
+ const ImageInfo& image_info,
+ bool* quick_is_interpreted) {
+ DCHECK(!method->IsResolutionMethod()) << method->PrettyMethod();
+ DCHECK_NE(method, Runtime::Current()->GetImtConflictMethod()) << method->PrettyMethod();
+ DCHECK(!method->IsImtUnimplementedMethod()) << method->PrettyMethod();
+ DCHECK(method->IsInvokable()) << method->PrettyMethod();
+ DCHECK(!IsInBootImage(method)) << method->PrettyMethod();
+
+ // Use original code if it exists. Otherwise, set the code pointer to the resolution
+ // trampoline.
+
+ // Quick entrypoint:
+ const void* quick_oat_entry_point =
+ method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_);
+ const uint8_t* quick_code;
+
+ if (UNLIKELY(IsInBootImage(method->GetDeclaringClass()))) {
+ DCHECK(method->IsCopied());
+ // If the code is not in the oat file corresponding to this image (e.g. default methods)
+ quick_code = reinterpret_cast<const uint8_t*>(quick_oat_entry_point);
+ } else {
+ uint32_t quick_oat_code_offset = PointerToLowMemUInt32(quick_oat_entry_point);
+ quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
+ }
+
+ *quick_is_interpreted = false;
+ if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() ||
+ method->GetDeclaringClass()->IsInitialized())) {
+ // We have code for a non-static or initialized method, just use the code.
+ } else if (quick_code == nullptr && method->IsNative() &&
+ (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) {
+ // Non-static or initialized native method missing compiled code, use generic JNI version.
+ quick_code = GetOatAddress(kOatAddressQuickGenericJNITrampoline);
+ } else if (quick_code == nullptr && !method->IsNative()) {
+ // We don't have code at all for a non-native method, use the interpreter.
+ quick_code = GetOatAddress(kOatAddressQuickToInterpreterBridge);
+ *quick_is_interpreted = true;
+ } else {
+ CHECK(!method->GetDeclaringClass()->IsInitialized());
+ // We have code for a static method, but need to go through the resolution stub for class
+ // initialization.
+ quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline);
+ }
+ if (!IsInBootOatFile(quick_code)) {
+ // DCHECK_GE(quick_code, oat_data_begin_);
+ }
+ return quick_code;
+}
+
+void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
+ ArtMethod* copy,
+ const ImageInfo& image_info) {
+ if (orig->IsAbstract()) {
+ // Ignore the single-implementation info for abstract method.
+ // Do this on orig instead of copy, otherwise there is a crash due to methods
+ // are copied before classes.
+ // TODO: handle fixup of single-implementation method for abstract method.
+ orig->SetHasSingleImplementation(false);
+ orig->SetSingleImplementation(
+ nullptr, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ }
+
+ memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
+
+ CopyReference(copy->GetDeclaringClassAddressWithoutBarrier(), orig->GetDeclaringClassUnchecked());
+
+ // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
+ // oat_begin_
+
+ // The resolution method has a special trampoline to call.
+ Runtime* runtime = Runtime::Current();
+ if (orig->IsRuntimeMethod()) {
+ ImtConflictTable* orig_table = orig->GetImtConflictTable(target_ptr_size_);
+ if (orig_table != nullptr) {
+ // Special IMT conflict method, normal IMT conflict method or unimplemented IMT method.
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(
+ GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_);
+ copy->SetImtConflictTable(NativeLocationInImage(orig_table), target_ptr_size_);
+ } else if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(
+ GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_);
+ } else {
+ bool found_one = false;
+ for (size_t i = 0; i < static_cast<size_t>(CalleeSaveType::kLastCalleeSaveType); ++i) {
+ auto idx = static_cast<CalleeSaveType>(i);
+ if (runtime->HasCalleeSaveMethod(idx) && runtime->GetCalleeSaveMethod(idx) == orig) {
+ found_one = true;
+ break;
+ }
+ }
+ CHECK(found_one) << "Expected to find callee save method but got " << orig->PrettyMethod();
+ CHECK(copy->IsRuntimeMethod());
+ }
+ } else {
+ // We assume all methods have code. If they don't currently then we set them to the use the
+ // resolution trampoline. Abstract methods never have code and so we need to make sure their
+ // use results in an AbstractMethodError. We use the interpreter to achieve this.
+ if (UNLIKELY(!orig->IsInvokable())) {
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(
+ GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_);
+ } else {
+ bool quick_is_interpreted;
+ const uint8_t* quick_code = GetQuickCode(orig, image_info, &quick_is_interpreted);
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(quick_code, target_ptr_size_);
+
+ // JNI entrypoint:
+ if (orig->IsNative()) {
+ // The native method's pointer is set to a stub to lookup via dlsym.
+ // Note this is not the code_ pointer, that is handled above.
+ copy->SetEntryPointFromJniPtrSize(
+ GetOatAddress(kOatAddressJNIDlsymLookup), target_ptr_size_);
+ }
+ }
+ }
+}
+
+size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const {
+ DCHECK_LE(up_to, kBinSize);
+ return std::accumulate(&image_info.bin_slot_sizes_[0],
+ &image_info.bin_slot_sizes_[up_to],
+ /*init*/0);
+}
+
+ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) {
+ // These values may need to get updated if more bins are added to the enum Bin
+ static_assert(kBinBits == 3, "wrong number of bin bits");
+ static_assert(kBinShift == 27, "wrong number of shift");
+ static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes");
+
+ DCHECK_LT(GetBin(), kBinSize);
+ DCHECK_ALIGNED(GetIndex(), kObjectAlignment);
+}
+
+ImageWriter::BinSlot::BinSlot(Bin bin, uint32_t index)
+ : BinSlot(index | (static_cast<uint32_t>(bin) << kBinShift)) {
+ DCHECK_EQ(index, GetIndex());
+}
+
+ImageWriter::Bin ImageWriter::BinSlot::GetBin() const {
+ return static_cast<Bin>((lockword_ & kBinMask) >> kBinShift);
+}
+
+uint32_t ImageWriter::BinSlot::GetIndex() const {
+ return lockword_ & ~kBinMask;
+}
+
+ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) {
+ switch (type) {
+ case kNativeObjectRelocationTypeArtField:
+ case kNativeObjectRelocationTypeArtFieldArray:
+ return kBinArtField;
+ case kNativeObjectRelocationTypeArtMethodClean:
+ case kNativeObjectRelocationTypeArtMethodArrayClean:
+ return kBinArtMethodClean;
+ case kNativeObjectRelocationTypeArtMethodDirty:
+ case kNativeObjectRelocationTypeArtMethodArrayDirty:
+ return kBinArtMethodDirty;
+ case kNativeObjectRelocationTypeDexCacheArray:
+ return kBinDexCacheArray;
+ case kNativeObjectRelocationTypeRuntimeMethod:
+ return kBinRuntimeMethod;
+ case kNativeObjectRelocationTypeIMTable:
+ return kBinImTable;
+ case kNativeObjectRelocationTypeIMTConflictTable:
+ return kBinIMTConflictTable;
+ }
+ UNREACHABLE();
+}
+
+size_t ImageWriter::GetOatIndex(mirror::Object* obj) const {
+ if (!IsMultiImage()) {
+ return GetDefaultOatIndex();
+ }
+ auto it = oat_index_map_.find(obj);
+ DCHECK(it != oat_index_map_.end()) << obj;
+ return it->second;
+}
+
+size_t ImageWriter::GetOatIndexForDexFile(const DexFile* dex_file) const {
+ if (!IsMultiImage()) {
+ return GetDefaultOatIndex();
+ }
+ auto it = dex_file_oat_index_map_.find(dex_file);
+ DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
+ return it->second;
+}
+
+size_t ImageWriter::GetOatIndexForDexCache(ObjPtr<mirror::DexCache> dex_cache) const {
+ return (dex_cache == nullptr)
+ ? GetDefaultOatIndex()
+ : GetOatIndexForDexFile(dex_cache->GetDexFile());
+}
+
+void ImageWriter::UpdateOatFileLayout(size_t oat_index,
+ size_t oat_loaded_size,
+ size_t oat_data_offset,
+ size_t oat_data_size) {
+ const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_;
+ for (const ImageInfo& info : image_infos_) {
+ DCHECK_LE(info.image_begin_ + info.image_size_, images_end);
+ }
+ DCHECK(images_end != nullptr); // Image space must be ready.
+
+ ImageInfo& cur_image_info = GetImageInfo(oat_index);
+ cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_;
+ cur_image_info.oat_loaded_size_ = oat_loaded_size;
+ cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset;
+ cur_image_info.oat_size_ = oat_data_size;
+
+ if (compile_app_image_) {
+ CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image.";
+ return;
+ }
+
+ // Update the oat_offset of the next image info.
+ if (oat_index + 1u != oat_filenames_.size()) {
+ // There is a following one.
+ ImageInfo& next_image_info = GetImageInfo(oat_index + 1u);
+ next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size;
+ }
+}
+
+void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header) {
+ ImageInfo& cur_image_info = GetImageInfo(oat_index);
+ cur_image_info.oat_checksum_ = oat_header.GetChecksum();
+
+ if (oat_index == GetDefaultOatIndex()) {
+ // Primary oat file, read the trampolines.
+ cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] =
+ oat_header.GetInterpreterToInterpreterBridgeOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] =
+ oat_header.GetInterpreterToCompiledCodeBridgeOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] =
+ oat_header.GetJniDlsymLookupOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] =
+ oat_header.GetQuickGenericJniTrampolineOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] =
+ oat_header.GetQuickImtConflictTrampolineOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] =
+ oat_header.GetQuickResolutionTrampolineOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] =
+ oat_header.GetQuickToInterpreterBridgeOffset();
+ }
+}
+
+ImageWriter::ImageWriter(
+ const CompilerDriver& compiler_driver,
+ uintptr_t image_begin,
+ bool compile_pic,
+ bool compile_app_image,
+ ImageHeader::StorageMode image_storage_mode,
+ const std::vector<const char*>& oat_filenames,
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects)
+ : compiler_driver_(compiler_driver),
+ global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
+ image_objects_offset_begin_(0),
+ compile_pic_(compile_pic),
+ compile_app_image_(compile_app_image),
+ target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
+ image_infos_(oat_filenames.size()),
+ dirty_methods_(0u),
+ clean_methods_(0u),
+ image_storage_mode_(image_storage_mode),
+ oat_filenames_(oat_filenames),
+ dex_file_oat_index_map_(dex_file_oat_index_map),
+ dirty_image_objects_(dirty_image_objects) {
+ CHECK_NE(image_begin, 0U);
+ std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
+ CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
+ << "Compiling a boot image should occur iff there are no boot image spaces loaded";
+}
+
+ImageWriter::ImageInfo::ImageInfo()
+ : intern_table_(new InternTable),
+ class_table_(new ClassTable) {}
+
+void ImageWriter::CopyReference(mirror::HeapReference<mirror::Object>* dest,
+ ObjPtr<mirror::Object> src) {
+ dest->Assign(GetImageAddress(src.Ptr()));
+}
+
+void ImageWriter::CopyReference(mirror::CompressedReference<mirror::Object>* dest,
+ ObjPtr<mirror::Object> src) {
+ dest->Assign(GetImageAddress(src.Ptr()));
+}
+
+void ImageWriter::CopyAndFixupPointer(void** target, void* value) {
+ void* new_value = value;
+ if (value != nullptr && !IsInBootImage(value)) {
+ auto it = native_object_relocations_.find(value);
+ CHECK(it != native_object_relocations_.end()) << value;
+ const NativeObjectRelocation& relocation = it->second;
+ ImageInfo& image_info = GetImageInfo(relocation.oat_index);
+ new_value = reinterpret_cast<void*>(image_info.image_begin_ + relocation.offset);
+ }
+ if (target_ptr_size_ == PointerSize::k32) {
+ *reinterpret_cast<uint32_t*>(target) = PointerToLowMemUInt32(new_value);
+ } else {
+ *reinterpret_cast<uint64_t*>(target) = reinterpret_cast<uintptr_t>(new_value);
+ }
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
new file mode 100644
index 0000000..bdea000
--- /dev/null
+++ b/dex2oat/linker/image_writer.h
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2011 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_DEX2OAT_LINKER_IMAGE_WRITER_H_
+#define ART_DEX2OAT_LINKER_IMAGE_WRITER_H_
+
+#include <stdint.h>
+#include "base/memory_tool.h"
+
+#include <cstddef>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <stack>
+#include <string>
+
+#include "art_method.h"
+#include "base/bit_utils.h"
+#include "base/dchecked_vector.h"
+#include "base/enums.h"
+#include "base/length_prefixed_array.h"
+#include "base/macros.h"
+#include "class_table.h"
+#include "driver/compiler_driver.h"
+#include "image.h"
+#include "intern_table.h"
+#include "lock_word.h"
+#include "mem_map.h"
+#include "mirror/dex_cache.h"
+#include "oat_file.h"
+#include "obj_ptr.h"
+#include "os.h"
+#include "safe_map.h"
+#include "utils.h"
+
+namespace art {
+namespace gc {
+namespace accounting {
+template <size_t kAlignment> class SpaceBitmap;
+typedef SpaceBitmap<kObjectAlignment> ContinuousSpaceBitmap;
+} // namespace accounting
+namespace space {
+class ImageSpace;
+} // namespace space
+} // namespace gc
+
+namespace mirror {
+class ClassLoader;
+} // namespace mirror
+
+class ClassLoaderVisitor;
+class ImtConflictTable;
+
+static constexpr int kInvalidFd = -1;
+
+namespace linker {
+
+// Write a Space built during compilation for use during execution.
+class ImageWriter FINAL {
+ public:
+ ImageWriter(const CompilerDriver& compiler_driver,
+ uintptr_t image_begin,
+ bool compile_pic,
+ bool compile_app_image,
+ ImageHeader::StorageMode image_storage_mode,
+ const std::vector<const char*>& oat_filenames,
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects);
+
+ bool PrepareImageAddressSpace();
+
+ bool IsImageAddressSpaceReady() const {
+ DCHECK(!image_infos_.empty());
+ for (const ImageInfo& image_info : image_infos_) {
+ if (image_info.image_roots_address_ == 0u) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ObjPtr<mirror::ClassLoader> GetClassLoader() {
+ CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
+ return compile_app_image_ ? *class_loaders_.begin() : nullptr;
+ }
+
+ template <typename T>
+ T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (object == nullptr || IsInBootImage(object)) {
+ return object;
+ } else {
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
+ return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object));
+ }
+ }
+
+ ArtMethod* GetImageMethodAddress(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ size_t GetOatFileOffset(size_t oat_index) const {
+ return GetImageInfo(oat_index).oat_offset_;
+ }
+
+ const uint8_t* GetOatFileBegin(size_t oat_index) const {
+ return GetImageInfo(oat_index).oat_file_begin_;
+ }
+
+ // If image_fd is not kInvalidFd, then we use that for the image file. Otherwise we open
+ // the names in image_filenames.
+ // If oat_fd is not kInvalidFd, then we use that for the oat file. Otherwise we open
+ // the names in oat_filenames.
+ bool Write(int image_fd,
+ const std::vector<const char*>& image_filenames,
+ const std::vector<const char*>& oat_filenames)
+ REQUIRES(!Locks::mutator_lock_);
+
+ uintptr_t GetOatDataBegin(size_t oat_index) {
+ return reinterpret_cast<uintptr_t>(GetImageInfo(oat_index).oat_data_begin_);
+ }
+
+ // Get the index of the oat file containing the dex file.
+ //
+ // This "oat_index" is used to retrieve information about the the memory layout
+ // of the oat file and its associated image file, needed for link-time patching
+ // of references to the image or across oat files.
+ size_t GetOatIndexForDexFile(const DexFile* dex_file) const;
+
+ // Get the index of the oat file containing the dex file served by the dex cache.
+ size_t GetOatIndexForDexCache(ObjPtr<mirror::DexCache> dex_cache) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Update the oat layout for the given oat file.
+ // This will make the oat_offset for the next oat file valid.
+ void UpdateOatFileLayout(size_t oat_index,
+ size_t oat_loaded_size,
+ size_t oat_data_offset,
+ size_t oat_data_size);
+ // Update information about the oat header, i.e. checksum and trampoline offsets.
+ void UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header);
+
+ private:
+ using WorkStack = std::stack<std::pair<mirror::Object*, size_t>>;
+
+ bool AllocMemory();
+
+ // Mark the objects defined in this space in the given live bitmap.
+ void RecordImageAllocations() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Classify different kinds of bins that objects end up getting packed into during image writing.
+ // Ordered from dirtiest to cleanest (until ArtMethods).
+ enum Bin {
+ kBinKnownDirty, // Known dirty objects from --dirty-image-objects list
+ kBinMiscDirty, // Dex caches, object locks, etc...
+ kBinClassVerified, // Class verified, but initializers haven't been run
+ // Unknown mix of clean/dirty:
+ kBinRegular,
+ kBinClassInitialized, // Class initializers have been run
+ // All classes get their own bins since their fields often dirty
+ kBinClassInitializedFinalStatics, // Class initializers have been run, no non-final statics
+ // Likely-clean:
+ kBinString, // [String] Almost always immutable (except for obj header).
+ // Add more bins here if we add more segregation code.
+ // Non mirror fields must be below.
+ // ArtFields should be always clean.
+ kBinArtField,
+ // If the class is initialized, then the ArtMethods are probably clean.
+ kBinArtMethodClean,
+ // ArtMethods may be dirty if the class has native methods or a declaring class that isn't
+ // initialized.
+ kBinArtMethodDirty,
+ // IMT (clean)
+ kBinImTable,
+ // Conflict tables (clean).
+ kBinIMTConflictTable,
+ // Runtime methods (always clean, do not have a length prefix array).
+ kBinRuntimeMethod,
+ // Dex cache arrays have a special slot for PC-relative addressing. Since they are
+ // huge, and as such their dirtiness is not important for the clean/dirty separation,
+ // we arbitrarily keep them at the end of the native data.
+ kBinDexCacheArray, // Arrays belonging to dex cache.
+ kBinSize,
+ // Number of bins which are for mirror objects.
+ kBinMirrorCount = kBinArtField,
+ };
+ friend std::ostream& operator<<(std::ostream& stream, const Bin& bin);
+
+ enum NativeObjectRelocationType {
+ kNativeObjectRelocationTypeArtField,
+ kNativeObjectRelocationTypeArtFieldArray,
+ kNativeObjectRelocationTypeArtMethodClean,
+ kNativeObjectRelocationTypeArtMethodArrayClean,
+ kNativeObjectRelocationTypeArtMethodDirty,
+ kNativeObjectRelocationTypeArtMethodArrayDirty,
+ kNativeObjectRelocationTypeRuntimeMethod,
+ kNativeObjectRelocationTypeIMTable,
+ kNativeObjectRelocationTypeIMTConflictTable,
+ kNativeObjectRelocationTypeDexCacheArray,
+ };
+ friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type);
+
+ enum OatAddress {
+ kOatAddressInterpreterToInterpreterBridge,
+ kOatAddressInterpreterToCompiledCodeBridge,
+ kOatAddressJNIDlsymLookup,
+ kOatAddressQuickGenericJNITrampoline,
+ kOatAddressQuickIMTConflictTrampoline,
+ kOatAddressQuickResolutionTrampoline,
+ kOatAddressQuickToInterpreterBridge,
+ // Number of elements in the enum.
+ kOatAddressCount,
+ };
+ friend std::ostream& operator<<(std::ostream& stream, const OatAddress& oat_address);
+
+ static constexpr size_t kBinBits = MinimumBitsToStore<uint32_t>(kBinMirrorCount - 1);
+ // uint32 = typeof(lockword_)
+ // Subtract read barrier bits since we want these to remain 0, or else it may result in DCHECK
+ // failures due to invalid read barrier bits during object field reads.
+ static const size_t kBinShift = BitSizeOf<uint32_t>() - kBinBits - LockWord::kGCStateSize;
+ // 111000.....0
+ static const size_t kBinMask = ((static_cast<size_t>(1) << kBinBits) - 1) << kBinShift;
+
+ // We use the lock word to store the bin # and bin index of the object in the image.
+ //
+ // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up
+ // stored in the lock word bit-for-bit when object forwarding addresses are being calculated.
+ struct BinSlot {
+ explicit BinSlot(uint32_t lockword);
+ BinSlot(Bin bin, uint32_t index);
+
+ // The bin an object belongs to, i.e. regular, class/verified, class/initialized, etc.
+ Bin GetBin() const;
+ // The offset in bytes from the beginning of the bin. Aligned to object size.
+ uint32_t GetIndex() const;
+ // Pack into a single uint32_t, for storing into a lock word.
+ uint32_t Uint32Value() const { return lockword_; }
+ // Comparison operator for map support
+ bool operator<(const BinSlot& other) const { return lockword_ < other.lockword_; }
+
+ private:
+ // Must be the same size as LockWord, any larger and we would truncate the data.
+ const uint32_t lockword_;
+ };
+
+ struct ImageInfo {
+ ImageInfo();
+ ImageInfo(ImageInfo&&) = default;
+
+ // Create the image sections into the out sections variable, returns the size of the image
+ // excluding the bitmap.
+ size_t CreateImageSections(ImageSection* out_sections, bool app_image) const;
+
+ std::unique_ptr<MemMap> image_; // Memory mapped for generating the image.
+
+ // Target begin of this image. Notes: It is not valid to write here, this is the address
+ // of the target image, not necessarily where image_ is mapped. The address is only valid
+ // after layouting (otherwise null).
+ uint8_t* image_begin_ = nullptr;
+
+ // Offset to the free space in image_, initially size of image header.
+ size_t image_end_ = RoundUp(sizeof(ImageHeader), kObjectAlignment);
+ uint32_t image_roots_address_ = 0; // The image roots address in the image.
+ size_t image_offset_ = 0; // Offset of this image from the start of the first image.
+
+ // Image size is the *address space* covered by this image. As the live bitmap is aligned
+ // to the page size, the live bitmap will cover more address space than necessary. But live
+ // bitmaps may not overlap, so an image has a "shadow," which is accounted for in the size.
+ // The next image may only start at image_begin_ + image_size_ (which is guaranteed to be
+ // page-aligned).
+ size_t image_size_ = 0;
+
+ // Oat data.
+ // Offset of the oat file for this image from start of oat files. This is
+ // valid when the previous oat file has been written.
+ size_t oat_offset_ = 0;
+ // Layout of the loaded ELF file containing the oat file, valid after UpdateOatFileLayout().
+ const uint8_t* oat_file_begin_ = nullptr;
+ size_t oat_loaded_size_ = 0;
+ const uint8_t* oat_data_begin_ = nullptr;
+ size_t oat_size_ = 0; // Size of the corresponding oat data.
+ // The oat header checksum, valid after UpdateOatFileHeader().
+ uint32_t oat_checksum_ = 0u;
+
+ // Image bitmap which lets us know where the objects inside of the image reside.
+ std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
+
+ // The start offsets of the dex cache arrays.
+ SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
+
+ // Offset from oat_data_begin_ to the stubs.
+ uint32_t oat_address_offsets_[kOatAddressCount] = {};
+
+ // Bin slot tracking for dirty object packing.
+ size_t bin_slot_sizes_[kBinSize] = {}; // Number of bytes in a bin.
+ size_t bin_slot_offsets_[kBinSize] = {}; // Number of bytes in previous bins.
+ size_t bin_slot_count_[kBinSize] = {}; // Number of objects in a bin.
+
+ // Cached size of the intern table for when we allocate memory.
+ size_t intern_table_bytes_ = 0;
+
+ // Number of image class table bytes.
+ size_t class_table_bytes_ = 0;
+
+ // Number of object fixup bytes.
+ size_t object_fixup_bytes_ = 0;
+
+ // Number of pointer fixup bytes.
+ size_t pointer_fixup_bytes_ = 0;
+
+ // Intern table associated with this image for serialization.
+ std::unique_ptr<InternTable> intern_table_;
+
+ // Class table associated with this image for serialization.
+ std::unique_ptr<ClassTable> class_table_;
+ };
+
+ // We use the lock word to store the offset of the object in the image.
+ void AssignImageOffset(mirror::Object* object, BinSlot bin_slot)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetImageOffset(mirror::Object* object, size_t offset)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsImageOffsetAssigned(mirror::Object* object) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ size_t GetImageOffset(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_);
+ void UpdateImageOffset(mirror::Object* obj, uintptr_t offset)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void PrepareDexCacheArraySlots() REQUIRES_SHARED(Locks::mutator_lock_);
+ void AssignImageBinSlot(mirror::Object* object, size_t oat_index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::Object* TryAssignBinSlot(WorkStack& work_stack, mirror::Object* obj, size_t oat_index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsImageBinSlotAssigned(mirror::Object* object) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ BinSlot GetImageBinSlot(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void AddDexCacheArrayRelocation(void* array, size_t offset, ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddMethodPointerArray(mirror::PointerArray* arr) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
+ }
+
+ mirror::Object* GetLocalAddress(mirror::Object* object) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t offset = GetImageOffset(object);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
+ uint8_t* dst = image_info.image_->Begin() + offset;
+ return reinterpret_cast<mirror::Object*>(dst);
+ }
+
+ // Returns the address in the boot image if we are compiling the app image.
+ const uint8_t* GetOatAddress(OatAddress type) const;
+
+ const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const {
+ // With Quick, code is within the OatFile, as there are all in one
+ // .o ELF object. But interpret it as signed.
+ DCHECK_LE(static_cast<int32_t>(offset), static_cast<int32_t>(image_info.oat_size_));
+ DCHECK(image_info.oat_data_begin_ != nullptr);
+ return offset == 0u ? nullptr : image_info.oat_data_begin_ + static_cast<int32_t>(offset);
+ }
+
+ // Returns true if the class was in the original requested image classes list.
+ bool KeepClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Debug aid that list of requested image classes.
+ void DumpImageClasses();
+
+ // Preinitializes some otherwise lazy fields (such as Class name) to avoid runtime image dirtying.
+ void ComputeLazyFieldsForImageClasses()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Visit all class loaders.
+ void VisitClassLoaders(ClassLoaderVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Remove unwanted classes from various roots.
+ void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Remove unwanted classes from the DexCache roots and preload deterministic DexCache contents.
+ void PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
+
+ // Verify unwanted classes removed.
+ void CheckNonImageClassesRemoved() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Lays out where the image objects will be at runtime.
+ void CalculateNewObjectOffsets()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void ProcessWorkStack(WorkStack* work_stack)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void CreateHeader(size_t oat_index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void CalculateObjectBinSlots(mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void UnbinObjectsIntoOffset(mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Creates the contiguous image in memory and adjusts pointers.
+ void CopyAndFixupNativeData(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_);
+ void CopyAndFixupObjects() REQUIRES_SHARED(Locks::mutator_lock_);
+ void CopyAndFixupObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+ void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void CopyAndFixupImTable(ImTable* orig, ImTable* copy) REQUIRES_SHARED(Locks::mutator_lock_);
+ void CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void FixupClass(mirror::Class* orig, mirror::Class* copy)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void FixupObject(mirror::Object* orig, mirror::Object* copy)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void FixupDexCache(mirror::DexCache* orig_dex_cache, mirror::DexCache* copy_dex_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void FixupPointerArray(mirror::Object* dst,
+ mirror::PointerArray* arr,
+ mirror::Class* klass,
+ Bin array_type)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Get quick code for non-resolution/imt_conflict/abstract method.
+ const uint8_t* GetQuickCode(ArtMethod* method,
+ const ImageInfo& image_info,
+ bool* quick_is_interpreted)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
+ size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const;
+
+ // Return true if a method is likely to be dirtied at runtime.
+ bool WillMethodBeDirty(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Assign the offset for an ArtMethod.
+ void AssignMethodOffset(ArtMethod* method,
+ NativeObjectRelocationType type,
+ size_t oat_index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Return true if imt was newly inserted.
+ bool TryAssignImTableOffset(ImTable* imt, size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Assign the offset for an IMT conflict table. Does nothing if the table already has a native
+ // relocation.
+ void TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Return true if klass is loaded by the boot class loader but not in the boot image.
+ bool IsBootClassLoaderNonImageClass(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Return true if klass depends on a boot class loader non image class. We want to prune these
+ // classes since we do not want any boot class loader classes in the image. This means that
+ // we also cannot have any classes which refer to these boot class loader non image classes.
+ // PruneAppImageClass also prunes if klass depends on a non-image class according to the compiler
+ // driver.
+ bool PruneAppImageClass(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // early_exit is true if we had a cyclic dependency anywhere down the chain.
+ bool PruneAppImageClassInternal(ObjPtr<mirror::Class> klass,
+ bool* early_exit,
+ std::unordered_set<mirror::Object*>* visited)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool IsMultiImage() const {
+ return image_infos_.size() > 1;
+ }
+
+ static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
+
+ uintptr_t NativeOffsetInImage(void* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Location of where the object will be when the image is loaded at runtime.
+ template <typename T>
+ T* NativeLocationInImage(T* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Location of where the temporary copy of the object currently is.
+ template <typename T>
+ T* NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Return true of obj is inside of the boot image space. This may only return true if we are
+ // compiling an app image.
+ bool IsInBootImage(const void* obj) const;
+
+ // Return true if ptr is within the boot oat file.
+ bool IsInBootOatFile(const void* ptr) const;
+
+ // Get the index of the oat file associated with the object.
+ size_t GetOatIndex(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // The oat index for shared data in multi-image and all data in single-image compilation.
+ size_t GetDefaultOatIndex() const {
+ return 0u;
+ }
+
+ ImageInfo& GetImageInfo(size_t oat_index) {
+ return image_infos_[oat_index];
+ }
+
+ const ImageInfo& GetImageInfo(size_t oat_index) const {
+ return image_infos_[oat_index];
+ }
+
+ // Find an already strong interned string in the other images or in the boot image. Used to
+ // remove duplicates in the multi image and app image case.
+ mirror::String* FindInternedString(mirror::String* string) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Return true if there already exists a native allocation for an object.
+ bool NativeRelocationAssigned(void* ptr) const;
+
+ void CopyReference(mirror::HeapReference<mirror::Object>* dest, ObjPtr<mirror::Object> src)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void CopyReference(mirror::CompressedReference<mirror::Object>* dest, ObjPtr<mirror::Object> src)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void CopyAndFixupPointer(void** target, void* value);
+
+ const CompilerDriver& compiler_driver_;
+
+ // Beginning target image address for the first image.
+ uint8_t* global_image_begin_;
+
+ // Offset from image_begin_ to where the first object is in image_.
+ size_t image_objects_offset_begin_;
+
+ // Pointer arrays that need to be updated. Since these are only some int and long arrays, we need
+ // to keep track. These include vtable arrays, iftable arrays, and dex caches.
+ std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_;
+
+ // Saved hash codes. We use these to restore lockwords which were temporarily used to have
+ // forwarding addresses as well as copying over hash codes.
+ std::unordered_map<mirror::Object*, uint32_t> saved_hashcode_map_;
+
+ // Oat index map for objects.
+ std::unordered_map<mirror::Object*, uint32_t> oat_index_map_;
+
+ // Boolean flags.
+ const bool compile_pic_;
+ const bool compile_app_image_;
+
+ // Size of pointers on the target architecture.
+ PointerSize target_ptr_size_;
+
+ // Image data indexed by the oat file index.
+ dchecked_vector<ImageInfo> image_infos_;
+
+ // ArtField, ArtMethod relocating map. These are allocated as array of structs but we want to
+ // have one entry per art field for convenience. ArtFields are placed right after the end of the
+ // image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields.
+ struct NativeObjectRelocation {
+ size_t oat_index;
+ uintptr_t offset;
+ NativeObjectRelocationType type;
+
+ bool IsArtMethodRelocation() const {
+ return type == kNativeObjectRelocationTypeArtMethodClean ||
+ type == kNativeObjectRelocationTypeArtMethodDirty ||
+ type == kNativeObjectRelocationTypeRuntimeMethod;
+ }
+ };
+ std::unordered_map<void*, NativeObjectRelocation> native_object_relocations_;
+
+ // Runtime ArtMethods which aren't reachable from any Class but need to be copied into the image.
+ ArtMethod* image_methods_[ImageHeader::kImageMethodsCount];
+
+ // Counters for measurements, used for logging only.
+ uint64_t dirty_methods_;
+ uint64_t clean_methods_;
+
+ // Prune class memoization table to speed up ContainsBootClassLoaderNonImageClass.
+ std::unordered_map<mirror::Class*, bool> prune_class_memo_;
+
+ // Class loaders with a class table to write out. There should only be one class loader because
+ // dex2oat loads the dex files to be compiled into a single class loader. For the boot image,
+ // null is a valid entry.
+ std::unordered_set<mirror::ClassLoader*> class_loaders_;
+
+ // Which mode the image is stored as, see image.h
+ const ImageHeader::StorageMode image_storage_mode_;
+
+ // The file names of oat files.
+ const std::vector<const char*>& oat_filenames_;
+
+ // Map of dex files to the indexes of oat files that they were compiled into.
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
+
+ // Set of objects known to be dirty in the image. Can be nullptr if there are none.
+ const std::unordered_set<std::string>* dirty_image_objects_;
+
+ class ComputeLazyFieldsForClassesVisitor;
+ class FixupClassVisitor;
+ class FixupRootVisitor;
+ class FixupVisitor;
+ class GetRootsVisitor;
+ class ImageAddressVisitorForDexCacheArray;
+ class NativeLocationVisitor;
+ class PruneClassesVisitor;
+ class PruneClassLoaderClassesVisitor;
+ class RegisterBootClassPathClassesVisitor;
+ class VisitReferencesVisitor;
+ class PruneObjectReferenceVisitor;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageWriter);
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_DEX2OAT_LINKER_IMAGE_WRITER_H_
diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc
new file mode 100644
index 0000000..178a78f
--- /dev/null
+++ b/dex2oat/linker/multi_oat_relative_patcher.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "multi_oat_relative_patcher.h"
+
+#include "base/bit_utils.h"
+#include "base/logging.h"
+#include "globals.h"
+
+namespace art {
+namespace linker {
+
+MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set,
+ const InstructionSetFeatures* features)
+ : method_offset_map_(),
+ relative_patcher_(RelativePatcher::Create(instruction_set, features, &method_offset_map_)),
+ adjustment_(0u),
+ instruction_set_(instruction_set),
+ start_size_code_alignment_(0u),
+ start_size_relative_call_thunks_(0u),
+ start_size_misc_thunks_(0u) {
+}
+
+void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) {
+ DCHECK_ALIGNED(adjustment, kPageSize);
+ adjustment_ = adjustment;
+
+ start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize();
+ start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize();
+ start_size_misc_thunks_ = relative_patcher_->MiscThunksSize();
+}
+
+uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const {
+ DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_);
+ return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_;
+}
+
+uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const {
+ DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_);
+ return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_;
+}
+
+uint32_t MultiOatRelativePatcher::MiscThunksSize() const {
+ DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_);
+ return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_;
+}
+
+std::pair<bool, uint32_t> MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset(
+ MethodReference ref) {
+ auto it = map.find(ref);
+ if (it == map.end()) {
+ return std::pair<bool, uint32_t>(false, 0u);
+ } else {
+ return std::pair<bool, uint32_t>(true, it->second);
+ }
+}
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h
new file mode 100644
index 0000000..6683366
--- /dev/null
+++ b/dex2oat/linker/multi_oat_relative_patcher.h
@@ -0,0 +1,158 @@
+/*
+ * 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_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
+#define ART_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
+
+#include "arch/instruction_set.h"
+#include "debug/method_debug_info.h"
+#include "method_reference.h"
+#include "linker/relative_patcher.h"
+#include "safe_map.h"
+
+namespace art {
+
+class CompiledMethod;
+class LinkerPatch;
+class InstructionSetFeatures;
+
+namespace linker {
+
+// MultiOatRelativePatcher is a helper class for handling patching across
+// any number of oat files. It provides storage for method code offsets
+// and wraps RelativePatcher calls, adjusting relative offsets according
+// to the value set by SetAdjustment().
+class MultiOatRelativePatcher FINAL {
+ public:
+ using const_iterator = SafeMap<MethodReference, uint32_t>::const_iterator;
+
+ MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features);
+
+ // Mark the start of a new oat file (for statistics retrieval) and set the
+ // adjustment for a new oat file to apply to all relative offsets that are
+ // passed to the MultiOatRelativePatcher.
+ //
+ // The adjustment should be the global offset of the base from which relative
+ // offsets are calculated, such as the start of .rodata for the current oat file.
+ // It must must never point directly to a method's code to avoid relative offsets
+ // with value 0 because this value is used as a missing offset indication in
+ // GetOffset() and an error indication in WriteThunks(). Additionally, it must be
+ // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP.
+ void StartOatFile(uint32_t adjustment);
+
+ // Get relative offset. Returns 0 when the offset has not been set yet.
+ uint32_t GetOffset(MethodReference method_ref) {
+ auto it = method_offset_map_.map.find(method_ref);
+ return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u;
+ }
+
+ // Set the offset.
+ void SetOffset(MethodReference method_ref, uint32_t offset) {
+ method_offset_map_.map.Put(method_ref, offset + adjustment_);
+ }
+
+ // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment.
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
+ MethodReference method_ref) {
+ offset += adjustment_;
+ offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref);
+ offset -= adjustment_;
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment.
+ uint32_t ReserveSpaceEnd(uint32_t offset) {
+ offset += adjustment_;
+ offset = relative_patcher_->ReserveSpaceEnd(offset);
+ offset -= adjustment_;
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment.
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) {
+ offset += adjustment_;
+ offset = relative_patcher_->WriteThunks(out, offset);
+ if (offset != 0u) { // 0u indicates write error.
+ offset -= adjustment_;
+ }
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment.
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ patch_offset += adjustment_;
+ target_offset += adjustment_;
+ relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset);
+ }
+
+ // Wrapper around RelativePatcher::PatchPcRelativeReference(), doing offset adjustment.
+ void PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ patch_offset += adjustment_;
+ target_offset += adjustment_;
+ relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset);
+ }
+
+ void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset) {
+ patch_offset += adjustment_;
+ relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset);
+ }
+
+ std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(size_t executable_offset) {
+ executable_offset += adjustment_;
+ return relative_patcher_->GenerateThunkDebugInfo(executable_offset);
+ }
+
+ // Wrappers around RelativePatcher for statistics retrieval.
+ uint32_t CodeAlignmentSize() const;
+ uint32_t RelativeCallThunksSize() const;
+ uint32_t MiscThunksSize() const;
+
+ private:
+ // Map method reference to assigned offset.
+ // Wrap the map in a class implementing RelativePatcherTargetProvider.
+ class MethodOffsetMap : public RelativePatcherTargetProvider {
+ public:
+ std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE;
+ SafeMap<MethodReference, uint32_t> map;
+ };
+
+ MethodOffsetMap method_offset_map_;
+ std::unique_ptr<RelativePatcher> relative_patcher_;
+ uint32_t adjustment_;
+ InstructionSet instruction_set_;
+
+ uint32_t start_size_code_alignment_;
+ uint32_t start_size_relative_call_thunks_;
+ uint32_t start_size_misc_thunks_;
+
+ friend class MultiOatRelativePatcherTest;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc
new file mode 100644
index 0000000..1b2d43e
--- /dev/null
+++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc
@@ -0,0 +1,309 @@
+/*
+ * 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 "multi_oat_relative_patcher.h"
+
+#include "compiled_method.h"
+#include "debug/method_debug_info.h"
+#include "gtest/gtest.h"
+#include "linker/vector_output_stream.h"
+
+namespace art {
+namespace linker {
+
+static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
+
+class MultiOatRelativePatcherTest : public testing::Test {
+ protected:
+ class MockPatcher : public RelativePatcher {
+ public:
+ MockPatcher() { }
+
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
+ MethodReference method_ref) OVERRIDE {
+ last_reserve_offset_ = offset;
+ last_reserve_method_ = method_ref;
+ offset += next_reserve_adjustment_;
+ next_reserve_adjustment_ = 0u;
+ return offset;
+ }
+
+ uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE {
+ last_reserve_offset_ = offset;
+ last_reserve_method_ = kNullMethodRef;
+ offset += next_reserve_adjustment_;
+ next_reserve_adjustment_ = 0u;
+ return offset;
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ last_write_offset_ = offset;
+ if (next_write_alignment_ != 0u) {
+ offset += next_write_alignment_;
+ bool success = WriteCodeAlignment(out, next_write_alignment_);
+ CHECK(success);
+ next_write_alignment_ = 0u;
+ }
+ if (next_write_call_thunk_ != 0u) {
+ offset += next_write_call_thunk_;
+ std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
+ bool success = WriteThunk(out, ArrayRef<const uint8_t>(thunk));
+ CHECK(success);
+ next_write_call_thunk_ = 0u;
+ }
+ if (next_write_misc_thunk_ != 0u) {
+ offset += next_write_misc_thunk_;
+ std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm');
+ bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk));
+ CHECK(success);
+ next_write_misc_thunk_ = 0u;
+ }
+ return offset;
+ }
+
+ void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ last_literal_offset_ = literal_offset;
+ last_patch_offset_ = patch_offset;
+ last_target_offset_ = target_offset;
+ }
+
+ void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ last_literal_offset_ = patch.LiteralOffset();
+ last_patch_offset_ = patch_offset;
+ last_target_offset_ = target_offset;
+ }
+
+ void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "UNIMPLEMENTED";
+ }
+
+ std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
+ uint32_t executable_offset ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "UNIMPLEMENTED";
+ UNREACHABLE();
+ }
+
+ uint32_t last_reserve_offset_ = 0u;
+ MethodReference last_reserve_method_ = kNullMethodRef;
+ uint32_t next_reserve_adjustment_ = 0u;
+
+ uint32_t last_write_offset_ = 0u;
+ uint32_t next_write_alignment_ = 0u;
+ uint32_t next_write_call_thunk_ = 0u;
+ uint32_t next_write_misc_thunk_ = 0u;
+
+ uint32_t last_literal_offset_ = 0u;
+ uint32_t last_patch_offset_ = 0u;
+ uint32_t last_target_offset_ = 0u;
+ };
+
+ MultiOatRelativePatcherTest()
+ : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
+ patcher_(kRuntimeISA, instruction_set_features_.get()) {
+ std::unique_ptr<MockPatcher> mock(new MockPatcher());
+ mock_ = mock.get();
+ patcher_.relative_patcher_ = std::move(mock);
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+ MultiOatRelativePatcher patcher_;
+ MockPatcher* mock_;
+};
+
+TEST_F(MultiOatRelativePatcherTest, Offsets) {
+ const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
+ MethodReference ref1(dex_file, 1u);
+ MethodReference ref2(dex_file, 2u);
+ EXPECT_EQ(0u, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+ EXPECT_EQ(0u, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t off1 = 0x1234;
+ patcher_.SetOffset(ref1, off1);
+ EXPECT_EQ(off1, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment2 = 0x30000;
+ patcher_.StartOatFile(adjustment2);
+ EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t off2 = 0x4321;
+ patcher_.SetOffset(ref2, off2);
+ EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
+ EXPECT_EQ(off2, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment3 = 0x78000;
+ patcher_.StartOatFile(adjustment3);
+ EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
+ EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
+}
+
+TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
+ const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
+ MethodReference ref1(dex_file, 1u);
+ MethodReference ref2(dex_file, 2u);
+ MethodReference ref3(dex_file, 3u);
+ const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_offset = 0x100;
+ uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
+ ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(ref1 == mock_->last_reserve_method_);
+ ASSERT_EQ(method1_offset, method1_offset_check);
+
+ uint32_t method2_offset = 0x1230;
+ uint32_t method2_reserve_adjustment = 0x10;
+ mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
+ uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
+ ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(ref2 == mock_->last_reserve_method_);
+ ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
+
+ uint32_t end1_offset = 0x4320;
+ uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
+ ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
+ ASSERT_EQ(end1_offset, end1_offset_check);
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_offset = 0xf00;
+ uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
+ ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(ref3 == mock_->last_reserve_method_);
+ ASSERT_EQ(method3_offset, method3_offset_check);
+
+ uint32_t end2_offset = 0x2400;
+ uint32_t end2_reserve_adjustment = 0x20;
+ mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
+ uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
+ ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_);
+ ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
+}
+
+TEST_F(MultiOatRelativePatcherTest, Write) {
+ std::vector<uint8_t> output;
+ VectorOutputStream vos("output", &output);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_offset = 0x100;
+ uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
+ ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method1_offset, method1_offset_check);
+ vos.WriteFully("1", 1); // Mark method1.
+
+ uint32_t method2_offset = 0x1230;
+ uint32_t method2_alignment_size = 1;
+ uint32_t method2_call_thunk_size = 2;
+ mock_->next_write_alignment_ = method2_alignment_size;
+ mock_->next_write_call_thunk_ = method2_call_thunk_size;
+ uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
+ ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
+ method2_offset_adjusted);
+ vos.WriteFully("2", 1); // Mark method2.
+
+ EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
+ EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_offset = 0xf00;
+ uint32_t method3_alignment_size = 2;
+ uint32_t method3_misc_thunk_size = 1;
+ mock_->next_write_alignment_ = method3_alignment_size;
+ mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
+ uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
+ ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
+ method3_offset_adjusted);
+ vos.WriteFully("3", 1); // Mark method3.
+
+ EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
+ EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
+
+ uint8_t expected_output[] = {
+ '1',
+ 0, 'c', 'c', '2',
+ 0, 0, 'm', '3',
+ };
+ ASSERT_EQ(arraysize(expected_output), output.size());
+ for (size_t i = 0; i != arraysize(expected_output); ++i) {
+ ASSERT_EQ(expected_output[i], output[i]) << i;
+ }
+}
+
+TEST_F(MultiOatRelativePatcherTest, Patch) {
+ std::vector<uint8_t> code(16);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_literal_offset = 4u;
+ uint32_t method1_patch_offset = 0x1234u;
+ uint32_t method1_target_offset = 0x8888u;
+ patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
+ DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
+ DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
+
+ uint32_t method2_literal_offset = 12u;
+ uint32_t method2_patch_offset = 0x7654u;
+ uint32_t method2_target_offset = 0xccccu;
+ LinkerPatch method2_patch =
+ LinkerPatch::StringBssEntryPatch(method2_literal_offset, nullptr, 0u, 1u);
+ patcher_.PatchPcRelativeReference(
+ &code, method2_patch, method2_patch_offset, method2_target_offset);
+ DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
+ DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_literal_offset = 8u;
+ uint32_t method3_patch_offset = 0x108u;
+ uint32_t method3_target_offset = 0x200u;
+ patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
+ DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
+ DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
new file mode 100644
index 0000000..51c2a03
--- /dev/null
+++ b/dex2oat/linker/oat_writer.cc
@@ -0,0 +1,3634 @@
+/*
+ * Copyright (C) 2011 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 "oat_writer.h"
+
+#include <unistd.h>
+#include <zlib.h>
+
+#include "arch/arm64/instruction_set_features_arm64.h"
+#include "art_method-inl.h"
+#include "base/allocator.h"
+#include "base/bit_vector-inl.h"
+#include "base/enums.h"
+#include "base/file_magic.h"
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+#include "class_linker.h"
+#include "class_table-inl.h"
+#include "compiled_method.h"
+#include "debug/method_debug_info.h"
+#include "dex/verification_results.h"
+#include "dex_file-inl.h"
+#include "dex_file_types.h"
+#include "dexlayout.h"
+#include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
+#include "gc/space/image_space.h"
+#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/method_bss_mapping_encoder.h"
+#include "linker/multi_oat_relative_patcher.h"
+#include "linker/output_stream.h"
+#include "mirror/array.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object-inl.h"
+#include "oat_quick_method_header.h"
+#include "os.h"
+#include "safe_map.h"
+#include "scoped_thread_state_change-inl.h"
+#include "type_lookup_table.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+#include "vdex_file.h"
+#include "verifier/verifier_deps.h"
+#include "zip_archive.h"
+
+namespace art {
+namespace linker {
+
+namespace { // anonymous namespace
+
+// If we write dex layout info in the oat file.
+static constexpr bool kWriteDexLayoutInfo = true;
+
+typedef DexFile::Header __attribute__((aligned(1))) UnalignedDexFileHeader;
+
+const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) {
+ return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data);
+}
+
+class ChecksumUpdatingOutputStream : public OutputStream {
+ public:
+ ChecksumUpdatingOutputStream(OutputStream* out, OatHeader* oat_header)
+ : OutputStream(out->GetLocation()), out_(out), oat_header_(oat_header) { }
+
+ bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE {
+ oat_header_->UpdateChecksum(buffer, byte_count);
+ return out_->WriteFully(buffer, byte_count);
+ }
+
+ off_t Seek(off_t offset, Whence whence) OVERRIDE {
+ return out_->Seek(offset, whence);
+ }
+
+ bool Flush() OVERRIDE {
+ return out_->Flush();
+ }
+
+ private:
+ OutputStream* const out_;
+ OatHeader* const oat_header_;
+};
+
+inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& compiled_method) {
+ // We want to align the code rather than the preheader.
+ uint32_t unaligned_code_offset = header_offset + sizeof(OatQuickMethodHeader);
+ uint32_t aligned_code_offset = compiled_method.AlignCode(unaligned_code_offset);
+ return aligned_code_offset - unaligned_code_offset;
+}
+
+} // anonymous namespace
+
+// Defines the location of the raw dex file to write.
+class OatWriter::DexFileSource {
+ public:
+ enum Type {
+ kNone,
+ kZipEntry,
+ kRawFile,
+ kRawData,
+ };
+
+ explicit DexFileSource(ZipEntry* zip_entry)
+ : type_(kZipEntry), source_(zip_entry) {
+ DCHECK(source_ != nullptr);
+ }
+
+ explicit DexFileSource(File* raw_file)
+ : type_(kRawFile), source_(raw_file) {
+ DCHECK(source_ != nullptr);
+ }
+
+ explicit DexFileSource(const uint8_t* dex_file)
+ : type_(kRawData), source_(dex_file) {
+ DCHECK(source_ != nullptr);
+ }
+
+ Type GetType() const { return type_; }
+ bool IsZipEntry() const { return type_ == kZipEntry; }
+ bool IsRawFile() const { return type_ == kRawFile; }
+ bool IsRawData() const { return type_ == kRawData; }
+
+ ZipEntry* GetZipEntry() const {
+ DCHECK(IsZipEntry());
+ DCHECK(source_ != nullptr);
+ return static_cast<ZipEntry*>(const_cast<void*>(source_));
+ }
+
+ File* GetRawFile() const {
+ DCHECK(IsRawFile());
+ DCHECK(source_ != nullptr);
+ return static_cast<File*>(const_cast<void*>(source_));
+ }
+
+ const uint8_t* GetRawData() const {
+ DCHECK(IsRawData());
+ DCHECK(source_ != nullptr);
+ return static_cast<const uint8_t*>(source_);
+ }
+
+ void Clear() {
+ type_ = kNone;
+ source_ = nullptr;
+ }
+
+ private:
+ Type type_;
+ const void* source_;
+};
+
+// OatClassHeader is the header only part of the oat class that is required even when compilation
+// is not enabled.
+class OatWriter::OatClassHeader {
+ public:
+ OatClassHeader(uint32_t offset,
+ uint32_t num_non_null_compiled_methods,
+ uint32_t num_methods,
+ mirror::Class::Status status)
+ : status_(status),
+ offset_(offset) {
+ // We just arbitrarily say that 0 methods means kOatClassNoneCompiled and that we won't use
+ // kOatClassAllCompiled unless there is at least one compiled method. This means in an
+ // interpreter only system, we can assert that all classes are kOatClassNoneCompiled.
+ if (num_non_null_compiled_methods == 0) {
+ type_ = kOatClassNoneCompiled;
+ } else if (num_non_null_compiled_methods == num_methods) {
+ type_ = kOatClassAllCompiled;
+ } else {
+ type_ = kOatClassSomeCompiled;
+ }
+ }
+
+ bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+ static size_t SizeOf() {
+ return sizeof(status_) + sizeof(type_);
+ }
+
+ // Data to write.
+ static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
+ int16_t status_;
+
+ static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
+ uint16_t type_;
+
+ // Offset of start of OatClass from beginning of OatHeader. It is
+ // used to validate file position when writing.
+ uint32_t offset_;
+};
+
+// The actual oat class body contains the information about compiled methods. It is only required
+// for compiler filters that have any compilation.
+class OatWriter::OatClass {
+ public:
+ OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
+ uint32_t compiled_methods_with_code,
+ uint16_t oat_class_type);
+ OatClass(OatClass&& src) = default;
+ size_t SizeOf() const;
+ bool Write(OatWriter* oat_writer, OutputStream* out) const;
+
+ CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
+ return compiled_methods_[class_def_method_index];
+ }
+
+ // CompiledMethods for each class_def_method_index, or null if no method is available.
+ dchecked_vector<CompiledMethod*> compiled_methods_;
+
+ // Offset from OatClass::offset_ to the OatMethodOffsets for the
+ // class_def_method_index. If 0, it means the corresponding
+ // CompiledMethod entry in OatClass::compiled_methods_ should be
+ // null and that the OatClass::type_ should be kOatClassBitmap.
+ dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
+
+ // Data to write.
+ uint32_t method_bitmap_size_;
+
+ // bit vector indexed by ClassDef method index. When
+ // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
+ // method has an OatMethodOffsets in methods_offsets_, otherwise
+ // the entry was ommited to save space. If OatClassType::type_ is
+ // not is kOatClassBitmap, the bitmap will be null.
+ std::unique_ptr<BitVector> method_bitmap_;
+
+ // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+ // present in the OatClass. Note that some may be missing if
+ // OatClass::compiled_methods_ contains null values (and
+ // oat_method_offsets_offsets_from_oat_class_ should contain 0
+ // values in this case).
+ dchecked_vector<OatMethodOffsets> method_offsets_;
+ dchecked_vector<OatQuickMethodHeader> method_headers_;
+
+ private:
+ size_t GetMethodOffsetsRawSize() const {
+ return method_offsets_.size() * sizeof(method_offsets_[0]);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(OatClass);
+};
+
+class OatWriter::OatDexFile {
+ public:
+ OatDexFile(const char* dex_file_location,
+ DexFileSource source,
+ CreateTypeLookupTable create_type_lookup_table);
+ OatDexFile(OatDexFile&& src) = default;
+
+ const char* GetLocation() const {
+ return dex_file_location_data_;
+ }
+
+ size_t SizeOf() const;
+ bool Write(OatWriter* oat_writer, OutputStream* out) const;
+ bool WriteClassOffsets(OatWriter* oat_writer, OutputStream* out);
+
+ size_t GetClassOffsetsRawSize() const {
+ return class_offsets_.size() * sizeof(class_offsets_[0]);
+ }
+
+ // The source of the dex file.
+ DexFileSource source_;
+
+ // Whether to create the type lookup table.
+ CreateTypeLookupTable create_type_lookup_table_;
+
+ // Dex file size. Initialized when writing the dex file.
+ size_t dex_file_size_;
+
+ // Offset of start of OatDexFile from beginning of OatHeader. It is
+ // used to validate file position when writing.
+ size_t offset_;
+
+ // Data to write.
+ uint32_t dex_file_location_size_;
+ const char* dex_file_location_data_;
+ uint32_t dex_file_location_checksum_;
+ uint32_t dex_file_offset_;
+ uint32_t class_offsets_offset_;
+ uint32_t lookup_table_offset_;
+ uint32_t method_bss_mapping_offset_;
+ uint32_t dex_sections_layout_offset_;
+
+ // Data to write to a separate section.
+ dchecked_vector<uint32_t> class_offsets_;
+
+ // Dex section layout info to serialize.
+ DexLayoutSections dex_sections_layout_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OatDexFile);
+};
+
+#define DCHECK_OFFSET() \
+ DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
+ << "file_offset=" << file_offset << " relative_offset=" << relative_offset
+
+#define DCHECK_OFFSET_() \
+ DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \
+ << "file_offset=" << file_offset << " offset_=" << offset_
+
+OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info)
+ : write_state_(WriteState::kAddingDexFileSources),
+ timings_(timings),
+ raw_dex_files_(),
+ zip_archives_(),
+ zipped_dex_files_(),
+ zipped_dex_file_locations_(),
+ compiler_driver_(nullptr),
+ image_writer_(nullptr),
+ compiling_boot_image_(compiling_boot_image),
+ dex_files_(nullptr),
+ vdex_size_(0u),
+ vdex_dex_files_offset_(0u),
+ vdex_verifier_deps_offset_(0u),
+ vdex_quickening_info_offset_(0u),
+ oat_size_(0u),
+ bss_start_(0u),
+ bss_size_(0u),
+ bss_methods_offset_(0u),
+ bss_roots_offset_(0u),
+ bss_method_entry_references_(),
+ bss_method_entries_(),
+ bss_type_entries_(),
+ bss_string_entries_(),
+ map_boot_image_tables_to_bss_(false),
+ oat_data_offset_(0u),
+ oat_header_(nullptr),
+ size_vdex_header_(0),
+ size_vdex_checksums_(0),
+ size_dex_file_alignment_(0),
+ size_executable_offset_alignment_(0),
+ size_oat_header_(0),
+ size_oat_header_key_value_store_(0),
+ size_dex_file_(0),
+ size_verifier_deps_(0),
+ size_verifier_deps_alignment_(0),
+ size_quickening_info_(0),
+ size_quickening_info_alignment_(0),
+ size_interpreter_to_interpreter_bridge_(0),
+ size_interpreter_to_compiled_code_bridge_(0),
+ size_jni_dlsym_lookup_(0),
+ size_quick_generic_jni_trampoline_(0),
+ size_quick_imt_conflict_trampoline_(0),
+ size_quick_resolution_trampoline_(0),
+ size_quick_to_interpreter_bridge_(0),
+ size_trampoline_alignment_(0),
+ size_method_header_(0),
+ size_code_(0),
+ size_code_alignment_(0),
+ size_relative_call_thunks_(0),
+ size_misc_thunks_(0),
+ size_vmap_table_(0),
+ size_method_info_(0),
+ size_oat_dex_file_location_size_(0),
+ size_oat_dex_file_location_data_(0),
+ size_oat_dex_file_location_checksum_(0),
+ size_oat_dex_file_offset_(0),
+ size_oat_dex_file_class_offsets_offset_(0),
+ size_oat_dex_file_lookup_table_offset_(0),
+ size_oat_dex_file_dex_layout_sections_offset_(0),
+ size_oat_dex_file_dex_layout_sections_(0),
+ size_oat_dex_file_dex_layout_sections_alignment_(0),
+ size_oat_dex_file_method_bss_mapping_offset_(0),
+ size_oat_lookup_table_alignment_(0),
+ size_oat_lookup_table_(0),
+ size_oat_class_offsets_alignment_(0),
+ size_oat_class_offsets_(0),
+ size_oat_class_type_(0),
+ size_oat_class_status_(0),
+ size_oat_class_method_bitmaps_(0),
+ size_oat_class_method_offsets_(0),
+ size_method_bss_mappings_(0u),
+ relative_patcher_(nullptr),
+ absolute_patch_locations_(),
+ profile_compilation_info_(info) {
+}
+
+bool OatWriter::AddDexFileSource(const char* filename,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table) {
+ DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+ uint32_t magic;
+ std::string error_msg;
+ File fd = OpenAndReadMagic(filename, &magic, &error_msg);
+ if (fd.Fd() == -1) {
+ PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'";
+ return false;
+ } else if (IsDexMagic(magic)) {
+ // The file is open for reading, not writing, so it's OK to let the File destructor
+ // close it without checking for explicit Close(), so pass checkUsage = false.
+ raw_dex_files_.emplace_back(new File(fd.Release(), location, /* checkUsage */ false));
+ oat_dex_files_.emplace_back(location,
+ DexFileSource(raw_dex_files_.back().get()),
+ create_type_lookup_table);
+ } else if (IsZipMagic(magic)) {
+ if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) {
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Expected valid zip or dex file: '" << filename << "'";
+ return false;
+ }
+ return true;
+}
+
+// Add dex file source(s) from a zip file specified by a file handle.
+bool OatWriter::AddZippedDexFilesSource(File&& zip_fd,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table) {
+ DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+ std::string error_msg;
+ zip_archives_.emplace_back(ZipArchive::OpenFromFd(zip_fd.Release(), location, &error_msg));
+ ZipArchive* zip_archive = zip_archives_.back().get();
+ if (zip_archive == nullptr) {
+ LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': "
+ << error_msg;
+ return false;
+ }
+ for (size_t i = 0; ; ++i) {
+ std::string entry_name = DexFile::GetMultiDexClassesDexName(i);
+ std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg));
+ if (entry == nullptr) {
+ break;
+ }
+ zipped_dex_files_.push_back(std::move(entry));
+ zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+ const char* full_location = zipped_dex_file_locations_.back().c_str();
+ oat_dex_files_.emplace_back(full_location,
+ DexFileSource(zipped_dex_files_.back().get()),
+ create_type_lookup_table);
+ }
+ if (zipped_dex_file_locations_.empty()) {
+ LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg;
+ return false;
+ }
+ return true;
+}
+
+// Add dex file source(s) from a vdex file specified by a file handle.
+bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table) {
+ DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+ const uint8_t* current_dex_data = nullptr;
+ for (size_t i = 0; i < vdex_file.GetHeader().GetNumberOfDexFiles(); ++i) {
+ current_dex_data = vdex_file.GetNextDexFileData(current_dex_data);
+ if (current_dex_data == nullptr) {
+ LOG(ERROR) << "Unexpected number of dex files in vdex " << location;
+ return false;
+ }
+ if (!DexFile::IsMagicValid(current_dex_data)) {
+ LOG(ERROR) << "Invalid magic in vdex file created from " << location;
+ return false;
+ }
+ // We used `zipped_dex_file_locations_` to keep the strings in memory.
+ zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+ const char* full_location = zipped_dex_file_locations_.back().c_str();
+ oat_dex_files_.emplace_back(full_location,
+ DexFileSource(current_dex_data),
+ create_type_lookup_table);
+ oat_dex_files_.back().dex_file_location_checksum_ = vdex_file.GetLocationChecksum(i);
+ }
+
+ if (vdex_file.GetNextDexFileData(current_dex_data) != nullptr) {
+ LOG(ERROR) << "Unexpected number of dex files in vdex " << location;
+ return false;
+ }
+
+ if (oat_dex_files_.empty()) {
+ LOG(ERROR) << "No dex files in vdex file created from " << location;
+ return false;
+ }
+ return true;
+}
+
+// Add dex file source from raw memory.
+bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data,
+ const char* location,
+ uint32_t location_checksum,
+ CreateTypeLookupTable create_type_lookup_table) {
+ DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+ if (data.size() < sizeof(DexFile::Header)) {
+ LOG(ERROR) << "Provided data is shorter than dex file header. size: "
+ << data.size() << " File: " << location;
+ return false;
+ }
+ if (!ValidateDexFileHeader(data.data(), location)) {
+ return false;
+ }
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(data.data());
+ if (data.size() < header->file_size_) {
+ LOG(ERROR) << "Truncated dex file data. Data size: " << data.size()
+ << " file size from header: " << header->file_size_ << " File: " << location;
+ return false;
+ }
+
+ oat_dex_files_.emplace_back(location, DexFileSource(data.data()), create_type_lookup_table);
+ oat_dex_files_.back().dex_file_location_checksum_ = location_checksum;
+ return true;
+}
+
+dchecked_vector<std::string> OatWriter::GetSourceLocations() const {
+ dchecked_vector<std::string> locations;
+ locations.reserve(oat_dex_files_.size());
+ for (const OatDexFile& oat_dex_file : oat_dex_files_) {
+ locations.push_back(oat_dex_file.GetLocation());
+ }
+ return locations;
+}
+
+bool OatWriter::MayHaveCompiledMethods() const {
+ return CompilerFilter::IsAnyCompilationEnabled(
+ GetCompilerDriver()->GetCompilerOptions().GetCompilerFilter());
+}
+
+bool OatWriter::WriteAndOpenDexFiles(
+ File* vdex_file,
+ OutputStream* oat_rodata,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures* instruction_set_features,
+ SafeMap<std::string, std::string>* key_value_store,
+ bool verify,
+ bool update_input_vdex,
+ /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+ /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
+ CHECK(write_state_ == WriteState::kAddingDexFileSources);
+
+ // 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;
+
+ // Initialize VDEX and OAT headers.
+ if (kIsVdexEnabled) {
+ // Reserve space for Vdex header and checksums.
+ vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum);
+ }
+ oat_size_ = InitOatHeader(instruction_set,
+ instruction_set_features,
+ dchecked_integral_cast<uint32_t>(oat_dex_files_.size()),
+ key_value_store);
+
+ ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get());
+
+ if (kIsVdexEnabled) {
+ std::unique_ptr<BufferedOutputStream> vdex_out =
+ std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
+ // Write DEX files into VDEX, mmap and open them.
+ if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) ||
+ !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
+ return false;
+ }
+ } else {
+ DCHECK(!update_input_vdex);
+ // Write DEX files into OAT, mmap and open them.
+ if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) ||
+ !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);
+ }
+ }
+
+ // Write type lookup tables into the oat file.
+ if (!WriteTypeLookupTables(&checksum_updating_rodata, dex_files)) {
+ return false;
+ }
+
+ // Write dex layout sections into the oat file.
+ if (!WriteDexLayoutSections(&checksum_updating_rodata, dex_files)) {
+ return false;
+ }
+
+ *opened_dex_files_map = std::move(dex_files_map);
+ *opened_dex_files = std::move(dex_files);
+ write_state_ = WriteState::kPrepareLayout;
+ return true;
+}
+
+void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) {
+ CHECK(write_state_ == WriteState::kPrepareLayout);
+
+ relative_patcher_ = relative_patcher;
+ SetMultiOatRelativePatcherAdjustment();
+
+ if (compiling_boot_image_) {
+ CHECK(image_writer_ != nullptr);
+ }
+ InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+ CHECK_EQ(instruction_set, oat_header_->GetInstructionSet());
+
+ {
+ TimingLogger::ScopedTiming split("InitBssLayout", timings_);
+ InitBssLayout(instruction_set);
+ }
+
+ uint32_t offset = oat_size_;
+ {
+ TimingLogger::ScopedTiming split("InitClassOffsets", timings_);
+ offset = InitClassOffsets(offset);
+ }
+ {
+ TimingLogger::ScopedTiming split("InitOatClasses", timings_);
+ offset = InitOatClasses(offset);
+ }
+ {
+ TimingLogger::ScopedTiming split("InitMethodBssMappings", timings_);
+ offset = InitMethodBssMappings(offset);
+ }
+ {
+ TimingLogger::ScopedTiming split("InitOatMaps", timings_);
+ offset = InitOatMaps(offset);
+ }
+ {
+ TimingLogger::ScopedTiming split("InitOatDexFiles", timings_);
+ oat_header_->SetOatDexFilesOffset(offset);
+ offset = InitOatDexFiles(offset);
+ }
+ {
+ TimingLogger::ScopedTiming split("InitOatCode", timings_);
+ offset = InitOatCode(offset);
+ }
+ {
+ TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_);
+ offset = InitOatCodeDexFiles(offset);
+ }
+ oat_size_ = offset;
+ bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u;
+
+ CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
+ if (compiling_boot_image_) {
+ CHECK_EQ(image_writer_ != nullptr,
+ oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr);
+ }
+
+ write_state_ = WriteState::kWriteRoData;
+}
+
+OatWriter::~OatWriter() {
+}
+
+class OatWriter::DexMethodVisitor {
+ public:
+ DexMethodVisitor(OatWriter* writer, size_t offset)
+ : writer_(writer),
+ offset_(offset),
+ dex_file_(nullptr),
+ class_def_index_(dex::kDexNoIndex) {}
+
+ virtual bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+ DCHECK(dex_file_ == nullptr);
+ DCHECK_EQ(class_def_index_, dex::kDexNoIndex);
+ dex_file_ = dex_file;
+ class_def_index_ = class_def_index;
+ return true;
+ }
+
+ virtual bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) = 0;
+
+ virtual bool EndClass() {
+ if (kIsDebugBuild) {
+ dex_file_ = nullptr;
+ class_def_index_ = dex::kDexNoIndex;
+ }
+ return true;
+ }
+
+ size_t GetOffset() const {
+ return offset_;
+ }
+
+ protected:
+ virtual ~DexMethodVisitor() { }
+
+ OatWriter* const writer_;
+
+ // The offset is usually advanced for each visited method by the derived class.
+ size_t offset_;
+
+ // The dex file and class def index are set in StartClass().
+ const DexFile* dex_file_;
+ size_t class_def_index_;
+};
+
+class OatWriter::OatDexMethodVisitor : public DexMethodVisitor {
+ public:
+ OatDexMethodVisitor(OatWriter* writer, size_t offset)
+ : DexMethodVisitor(writer, offset),
+ oat_class_index_(0u),
+ method_offsets_index_(0u) {}
+
+ bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE {
+ DexMethodVisitor::StartClass(dex_file, class_def_index);
+ if (kIsDebugBuild && writer_->MayHaveCompiledMethods()) {
+ // There are no oat classes if there aren't any compiled methods.
+ CHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+ }
+ method_offsets_index_ = 0u;
+ return true;
+ }
+
+ bool EndClass() OVERRIDE {
+ ++oat_class_index_;
+ return DexMethodVisitor::EndClass();
+ }
+
+ protected:
+ size_t oat_class_index_;
+ size_t method_offsets_index_;
+};
+
+static bool HasCompiledCode(const CompiledMethod* method) {
+ // The dextodexcompiler puts the quickening info table into the CompiledMethod
+ // for simplicity. For such methods, we will emit an OatQuickMethodHeader
+ // only when vdex is disabled.
+ return method != nullptr && (!method->GetQuickCode().empty() || !kIsVdexEnabled);
+}
+
+static bool HasQuickeningInfo(const CompiledMethod* method) {
+ return method != nullptr && method->GetQuickCode().empty() && !method->GetVmapTable().empty();
+}
+
+class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor {
+ public:
+ explicit InitBssLayoutMethodVisitor(OatWriter* writer)
+ : DexMethodVisitor(writer, /* offset */ 0u) {}
+
+ bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED,
+ const ClassDataItemIterator& it) OVERRIDE {
+ // Look for patches with .bss references and prepare maps with placeholders for their offsets.
+ CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(
+ MethodReference(dex_file_, it.GetMemberIndex()));
+ if (HasCompiledCode(compiled_method)) {
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) {
+ MethodReference target_method = patch.TargetMethod();
+ auto refs_it = writer_->bss_method_entry_references_.find(target_method.dex_file);
+ if (refs_it == writer_->bss_method_entry_references_.end()) {
+ refs_it = writer_->bss_method_entry_references_.Put(
+ target_method.dex_file,
+ BitVector(target_method.dex_file->NumMethodIds(),
+ /* expandable */ false,
+ Allocator::GetMallocAllocator()));
+ refs_it->second.ClearAllBits();
+ }
+ refs_it->second.SetBit(target_method.index);
+ writer_->bss_method_entries_.Overwrite(target_method, /* placeholder */ 0u);
+ } else if (patch.GetType() == LinkerPatch::Type::kTypeBssEntry) {
+ TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+ writer_->bss_type_entries_.Overwrite(ref, /* placeholder */ 0u);
+ } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) {
+ StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex());
+ writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u);
+ } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable ||
+ patch.GetType() == LinkerPatch::Type::kTypeClassTable) {
+ writer_->map_boot_image_tables_to_bss_ = true;
+ }
+ }
+ } else {
+ DCHECK(compiled_method == nullptr || compiled_method->GetPatches().empty());
+ }
+ return true;
+ }
+};
+
+class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
+ public:
+ InitOatClassesMethodVisitor(OatWriter* writer, size_t offset)
+ : DexMethodVisitor(writer, offset),
+ compiled_methods_(),
+ compiled_methods_with_code_(0u) {
+ size_t num_classes = 0u;
+ for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) {
+ num_classes += oat_dex_file.class_offsets_.size();
+ }
+ // If we aren't compiling only reserve headers.
+ writer_->oat_class_headers_.reserve(num_classes);
+ if (writer->MayHaveCompiledMethods()) {
+ writer->oat_classes_.reserve(num_classes);
+ }
+ compiled_methods_.reserve(256u);
+ // If there are any classes, the class offsets allocation aligns the offset.
+ DCHECK(num_classes == 0u || IsAligned<4u>(offset));
+ }
+
+ bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE {
+ DexMethodVisitor::StartClass(dex_file, class_def_index);
+ compiled_methods_.clear();
+ compiled_methods_with_code_ = 0u;
+ return true;
+ }
+
+ bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED,
+ const ClassDataItemIterator& it) OVERRIDE {
+ // Fill in the compiled_methods_ array for methods that have a
+ // CompiledMethod. We track the number of non-null entries in
+ // compiled_methods_with_code_ since we only want to allocate
+ // OatMethodOffsets for the compiled methods.
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx));
+ compiled_methods_.push_back(compiled_method);
+ if (HasCompiledCode(compiled_method)) {
+ ++compiled_methods_with_code_;
+ }
+ return true;
+ }
+
+ bool EndClass() OVERRIDE {
+ ClassReference class_ref(dex_file_, class_def_index_);
+ mirror::Class::Status status;
+ bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status);
+ if (!found) {
+ VerificationResults* results = writer_->compiler_driver_->GetVerificationResults();
+ if (results != nullptr && results->IsClassRejected(class_ref)) {
+ // The oat class status is used only for verification of resolved classes,
+ // so use kStatusErrorResolved whether the class was resolved or unresolved
+ // during compile-time verification.
+ status = mirror::Class::kStatusErrorResolved;
+ } else {
+ status = mirror::Class::kStatusNotReady;
+ }
+ }
+
+ writer_->oat_class_headers_.emplace_back(offset_,
+ compiled_methods_with_code_,
+ compiled_methods_.size(),
+ status);
+ OatClassHeader& header = writer_->oat_class_headers_.back();
+ offset_ += header.SizeOf();
+ if (writer_->MayHaveCompiledMethods()) {
+ writer_->oat_classes_.emplace_back(compiled_methods_,
+ compiled_methods_with_code_,
+ header.type_);
+ offset_ += writer_->oat_classes_.back().SizeOf();
+ }
+ return DexMethodVisitor::EndClass();
+ }
+
+ private:
+ dchecked_vector<CompiledMethod*> compiled_methods_;
+ size_t compiled_methods_with_code_;
+};
+
+class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
+ public:
+ InitCodeMethodVisitor(OatWriter* writer, size_t offset)
+ : InitCodeMethodVisitor(writer, offset, writer->GetCompilerDriver()->GetCompilerOptions()) {}
+
+ bool EndClass() OVERRIDE {
+ OatDexMethodVisitor::EndClass();
+ if (oat_class_index_ == writer_->oat_classes_.size()) {
+ offset_ = relative_patcher_->ReserveSpaceEnd(offset_);
+ if (generate_debug_info_) {
+ std::vector<debug::MethodDebugInfo> thunk_infos =
+ relative_patcher_->GenerateThunkDebugInfo(executable_offset_);
+ writer_->method_info_.insert(writer_->method_info_.end(),
+ std::make_move_iterator(thunk_infos.begin()),
+ std::make_move_iterator(thunk_infos.end()));
+ }
+ }
+ return true;
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (HasCompiledCode(compiled_method)) {
+ // Derived from CompiledMethod.
+ uint32_t quick_code_offset = 0;
+
+ ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
+ uint32_t code_size = quick_code.size() * sizeof(uint8_t);
+ uint32_t thumb_offset = compiled_method->CodeDelta();
+
+ // Deduplicate code arrays if we are not producing debuggable code.
+ bool deduped = true;
+ MethodReference method_ref(dex_file_, it.GetMemberIndex());
+ if (debuggable_) {
+ quick_code_offset = relative_patcher_->GetOffset(method_ref);
+ if (quick_code_offset != 0u) {
+ // Duplicate methods, we want the same code for both of them so that the oat writer puts
+ // the same code in both ArtMethods so that we do not get different oat code at runtime.
+ } else {
+ quick_code_offset = NewQuickCodeOffset(compiled_method, it, thumb_offset);
+ deduped = false;
+ }
+ } else {
+ quick_code_offset = dedupe_map_.GetOrCreate(
+ compiled_method,
+ [this, &deduped, compiled_method, &it, thumb_offset]() {
+ deduped = false;
+ return NewQuickCodeOffset(compiled_method, it, thumb_offset);
+ });
+ }
+
+ if (code_size != 0) {
+ if (relative_patcher_->GetOffset(method_ref) != 0u) {
+ // TODO: Should this be a hard failure?
+ LOG(WARNING) << "Multiple definitions of "
+ << method_ref.PrettyMethod()
+ << " offsets " << relative_patcher_->GetOffset(method_ref)
+ << " " << quick_code_offset;
+ } else {
+ relative_patcher_->SetOffset(method_ref, quick_code_offset);
+ }
+ }
+
+ // Update quick method header.
+ DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+ OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+ uint32_t vmap_table_offset = method_header->GetVmapTableOffset();
+ uint32_t method_info_offset = method_header->GetMethodInfoOffset();
+ // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+ // to 0-offset and we need to adjust it by code_offset.
+ uint32_t code_offset = quick_code_offset - thumb_offset;
+ if (!compiled_method->GetQuickCode().empty()) {
+ // If the code is compiled, we write the offset of the stack map relative
+ // to the code,
+ if (vmap_table_offset != 0u) {
+ vmap_table_offset += code_offset;
+ DCHECK_LT(vmap_table_offset, code_offset);
+ }
+ if (method_info_offset != 0u) {
+ method_info_offset += code_offset;
+ DCHECK_LT(method_info_offset, code_offset);
+ }
+ } else {
+ CHECK(!kIsVdexEnabled);
+ // We write the offset of the quickening info relative to the code.
+ vmap_table_offset += code_offset;
+ DCHECK_LT(vmap_table_offset, code_offset);
+ }
+ uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
+ uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
+ uint32_t fp_spill_mask = compiled_method->GetFpSpillMask();
+ *method_header = OatQuickMethodHeader(vmap_table_offset,
+ method_info_offset,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ code_size);
+
+ if (!deduped) {
+ // Update offsets. (Checksum is updated when writing.)
+ offset_ += sizeof(*method_header); // Method header is prepended before code.
+ offset_ += code_size;
+ // Record absolute patch locations.
+ if (!compiled_method->GetPatches().empty()) {
+ uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (!patch.IsPcRelative()) {
+ writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
+ }
+ }
+ }
+ }
+
+ // Exclude quickened dex methods (code_size == 0) since they have no native code.
+ if (generate_debug_info_ && code_size != 0) {
+ bool has_code_info = method_header->IsOptimized();
+ // Record debug information for this function if we are doing that.
+ debug::MethodDebugInfo info = {};
+ DCHECK(info.trampoline_name.empty());
+ info.dex_file = dex_file_;
+ info.class_def_index = class_def_index_;
+ info.dex_method_index = it.GetMemberIndex();
+ info.access_flags = it.GetMethodAccessFlags();
+ info.code_item = it.GetMethodCodeItem();
+ info.isa = compiled_method->GetInstructionSet();
+ info.deduped = deduped;
+ info.is_native_debuggable = native_debuggable_;
+ info.is_optimized = method_header->IsOptimized();
+ info.is_code_address_text_relative = true;
+ info.code_address = code_offset - executable_offset_;
+ info.code_size = code_size;
+ info.frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
+ info.code_info = has_code_info ? compiled_method->GetVmapTable().data() : nullptr;
+ info.cfi = compiled_method->GetCFIInfo();
+ writer_->method_info_.push_back(info);
+ }
+
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ OatMethodOffsets* offsets = &oat_class->method_offsets_[method_offsets_index_];
+ offsets->code_offset_ = quick_code_offset;
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ InitCodeMethodVisitor(OatWriter* writer, size_t offset, const CompilerOptions& compiler_options)
+ : OatDexMethodVisitor(writer, offset),
+ relative_patcher_(writer->relative_patcher_),
+ executable_offset_(writer->oat_header_->GetExecutableOffset()),
+ debuggable_(compiler_options.GetDebuggable()),
+ native_debuggable_(compiler_options.GetNativeDebuggable()),
+ generate_debug_info_(compiler_options.GenerateAnyDebugInfo()) {
+ writer->absolute_patch_locations_.reserve(
+ writer->GetCompilerDriver()->GetNonRelativeLinkerPatchCount());
+ }
+
+ struct CodeOffsetsKeyComparator {
+ bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+ // Code is deduplicated by CompilerDriver, compare only data pointers.
+ if (lhs->GetQuickCode().data() != rhs->GetQuickCode().data()) {
+ return lhs->GetQuickCode().data() < rhs->GetQuickCode().data();
+ }
+ // If the code is the same, all other fields are likely to be the same as well.
+ if (UNLIKELY(lhs->GetVmapTable().data() != rhs->GetVmapTable().data())) {
+ return lhs->GetVmapTable().data() < rhs->GetVmapTable().data();
+ }
+ if (UNLIKELY(lhs->GetMethodInfo().data() != rhs->GetMethodInfo().data())) {
+ return lhs->GetMethodInfo().data() < rhs->GetMethodInfo().data();
+ }
+ if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) {
+ return lhs->GetPatches().data() < rhs->GetPatches().data();
+ }
+ return false;
+ }
+ };
+
+ uint32_t NewQuickCodeOffset(CompiledMethod* compiled_method,
+ const ClassDataItemIterator& it,
+ uint32_t thumb_offset) {
+ offset_ = relative_patcher_->ReserveSpace(
+ offset_, compiled_method, MethodReference(dex_file_, it.GetMemberIndex()));
+ offset_ += CodeAlignmentSize(offset_, *compiled_method);
+ DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ return offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
+ }
+
+ // Deduplication is already done on a pointer basis by the compiler driver,
+ // so we can simply compare the pointers to find out if things are duplicated.
+ SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
+
+ // Cache writer_'s members and compiler options.
+ linker::MultiOatRelativePatcher* relative_patcher_;
+ uint32_t executable_offset_;
+ const bool debuggable_;
+ const bool native_debuggable_;
+ const bool generate_debug_info_;
+};
+
+class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
+ public:
+ InitMapMethodVisitor(OatWriter* writer, size_t offset)
+ : OatDexMethodVisitor(writer, offset) {}
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (HasCompiledCode(compiled_method)) {
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetVmapTableOffset(), 0u);
+
+ ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
+ uint32_t map_size = map.size() * sizeof(map[0]);
+ if (map_size != 0u) {
+ size_t offset = dedupe_map_.GetOrCreate(
+ map.data(),
+ [this, map_size]() {
+ uint32_t new_offset = offset_;
+ offset_ += map_size;
+ return new_offset;
+ });
+ // Code offset is not initialized yet, so set the map offset to 0u-offset.
+ DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u);
+ oat_class->method_headers_[method_offsets_index_].SetVmapTableOffset(0u - offset);
+ }
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ // Deduplication is already done on a pointer basis by the compiler driver,
+ // so we can simply compare the pointers to find out if things are duplicated.
+ SafeMap<const uint8_t*, uint32_t> dedupe_map_;
+};
+
+class OatWriter::InitMethodInfoVisitor : public OatDexMethodVisitor {
+ public:
+ InitMethodInfoVisitor(OatWriter* writer, size_t offset) : OatDexMethodVisitor(writer, offset) {}
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (HasCompiledCode(compiled_method)) {
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset(), 0u);
+ ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo();
+ const uint32_t map_size = map.size() * sizeof(map[0]);
+ if (map_size != 0u) {
+ size_t offset = dedupe_map_.GetOrCreate(
+ map.data(),
+ [this, map_size]() {
+ uint32_t new_offset = offset_;
+ offset_ += map_size;
+ return new_offset;
+ });
+ // Code offset is not initialized yet, so set the map offset to 0u-offset.
+ DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u);
+ oat_class->method_headers_[method_offsets_index_].SetMethodInfoOffset(0u - offset);
+ }
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ // Deduplication is already done on a pointer basis by the compiler driver,
+ // so we can simply compare the pointers to find out if things are duplicated.
+ SafeMap<const uint8_t*, uint32_t> dedupe_map_;
+};
+
+class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
+ public:
+ InitImageMethodVisitor(OatWriter* writer,
+ size_t offset,
+ const std::vector<const DexFile*>* dex_files)
+ : OatDexMethodVisitor(writer, offset),
+ pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())),
+ class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
+ dex_files_(dex_files),
+ class_linker_(Runtime::Current()->GetClassLinker()) {}
+
+ // Handle copied methods here. Copy pointer to quick code from
+ // an origin method to a copied method only if they are
+ // in the same oat file. If the origin and the copied methods are
+ // in different oat files don't touch the copied method.
+ // References to other oat files are not supported yet.
+ bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ OatDexMethodVisitor::StartClass(dex_file, class_def_index);
+ // Skip classes that are not in the image.
+ if (!IsImageClass()) {
+ return true;
+ }
+ ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file);
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_);
+ if (klass != nullptr) {
+ for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) {
+ // Find origin method. Declaring class and dex_method_idx
+ // in the copied method should be the same as in the origin
+ // method.
+ mirror::Class* declaring_class = method.GetDeclaringClass();
+ ArtMethod* origin = declaring_class->FindClassMethod(
+ declaring_class->GetDexCache(),
+ method.GetDexMethodIndex(),
+ pointer_size_);
+ CHECK(origin != nullptr);
+ CHECK(!origin->IsDirect());
+ CHECK(origin->GetDeclaringClass() == declaring_class);
+ if (IsInOatFile(&declaring_class->GetDexFile())) {
+ const void* code_ptr =
+ origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+ if (code_ptr == nullptr) {
+ methods_to_process_.push_back(std::make_pair(&method, origin));
+ } else {
+ method.SetEntryPointFromQuickCompiledCodePtrSize(
+ code_ptr, pointer_size_);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Skip methods that are not in the image.
+ if (!IsImageClass()) {
+ return true;
+ }
+
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ OatMethodOffsets offsets(0u);
+ if (HasCompiledCode(compiled_method)) {
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ offsets = oat_class->method_offsets_[method_offsets_index_];
+ ++method_offsets_index_;
+ }
+
+ Thread* self = Thread::Current();
+ ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_);
+ ArtMethod* method;
+ if (writer_->HasBootImage()) {
+ const InvokeType invoke_type = it.GetMethodInvokeType(
+ dex_file_->GetClassDef(class_def_index_));
+ // Unchecked as we hold mutator_lock_ on entry.
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<1> hs(self);
+ method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
+ *dex_file_,
+ it.GetMemberIndex(),
+ hs.NewHandle(dex_cache),
+ ScopedNullHandle<mirror::ClassLoader>(),
+ nullptr,
+ invoke_type);
+ if (method == nullptr) {
+ LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: "
+ << dex_file_->PrettyMethod(it.GetMemberIndex(), true);
+ self->AssertPendingException();
+ mirror::Throwable* exc = self->GetException();
+ std::string dump = exc->Dump();
+ LOG(FATAL) << dump;
+ UNREACHABLE();
+ }
+ } else {
+ // Should already have been resolved by the compiler.
+ // It may not be resolved if the class failed to verify, in this case, don't set the
+ // entrypoint. This is not fatal since we shall use a resolution method.
+ method = class_linker_->LookupResolvedMethod(it.GetMemberIndex(), dex_cache, class_loader_);
+ }
+ if (method != nullptr &&
+ compiled_method != nullptr &&
+ compiled_method->GetQuickCode().size() != 0) {
+ method->SetEntryPointFromQuickCompiledCodePtrSize(
+ reinterpret_cast<void*>(offsets.code_offset_), pointer_size_);
+ }
+
+ return true;
+ }
+
+ // Check whether current class is image class
+ bool IsImageClass() {
+ const DexFile::TypeId& type_id =
+ dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_);
+ const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id);
+ return writer_->GetCompilerDriver()->IsImageClass(class_descriptor);
+ }
+
+ // Check whether specified dex file is in the compiled oat file.
+ bool IsInOatFile(const DexFile* dex_file) {
+ return ContainsElement(*dex_files_, dex_file);
+ }
+
+ // Assign a pointer to quick code for copied methods
+ // not handled in the method StartClass
+ void Postprocess() {
+ for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) {
+ ArtMethod* method = p.first;
+ ArtMethod* origin = p.second;
+ const void* code_ptr =
+ origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+ if (code_ptr != nullptr) {
+ method->SetEntryPointFromQuickCompiledCodePtrSize(code_ptr, pointer_size_);
+ }
+ }
+ }
+
+ private:
+ const PointerSize pointer_size_;
+ ObjPtr<mirror::ClassLoader> class_loader_;
+ const std::vector<const DexFile*>* dex_files_;
+ ClassLinker* const class_linker_;
+ std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_;
+};
+
+class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
+ public:
+ WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
+ size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
+ : OatDexMethodVisitor(writer, relative_offset),
+ pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())),
+ class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
+ out_(out),
+ file_offset_(file_offset),
+ soa_(Thread::Current()),
+ no_thread_suspension_("OatWriter patching"),
+ class_linker_(Runtime::Current()->GetClassLinker()),
+ dex_cache_(nullptr) {
+ patched_code_.reserve(16 * KB);
+ if (writer_->HasBootImage()) {
+ // If we're creating the image, the address space must be ready so that we can apply patches.
+ CHECK(writer_->image_writer_->IsImageAddressSpaceReady());
+ }
+ }
+
+ ~WriteCodeMethodVisitor() UNLOCK_FUNCTION(Locks::mutator_lock_) {
+ }
+
+ bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ OatDexMethodVisitor::StartClass(dex_file, class_def_index);
+ if (writer_->GetCompilerDriver()->GetCompilerOptions().IsAotCompilationEnabled()) {
+ // Only need to set the dex cache if we have compilation. Other modes might have unloaded it.
+ if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
+ dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file);
+ DCHECK(dex_cache_ != nullptr);
+ }
+ }
+ return true;
+ }
+
+ bool EndClass() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool result = OatDexMethodVisitor::EndClass();
+ if (oat_class_index_ == writer_->oat_classes_.size()) {
+ DCHECK(result); // OatDexMethodVisitor::EndClass() never fails.
+ offset_ = writer_->relative_patcher_->WriteThunks(out_, offset_);
+ if (UNLIKELY(offset_ == 0u)) {
+ PLOG(ERROR) << "Failed to write final relative call thunks";
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ // No thread suspension since dex_cache_ that may get invalidated if that occurs.
+ ScopedAssertNoThreadSuspension tsc(__FUNCTION__);
+ if (HasCompiledCode(compiled_method)) {
+ size_t file_offset = file_offset_;
+ OutputStream* out = out_;
+
+ ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
+ uint32_t code_size = quick_code.size() * sizeof(uint8_t);
+
+ // Deduplicate code arrays.
+ const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_];
+ if (method_offsets.code_offset_ > offset_) {
+ offset_ = writer_->relative_patcher_->WriteThunks(out, offset_);
+ if (offset_ == 0u) {
+ ReportWriteFailure("relative call thunk", it);
+ return false;
+ }
+ uint32_t alignment_size = CodeAlignmentSize(offset_, *compiled_method);
+ if (alignment_size != 0) {
+ if (!writer_->WriteCodeAlignment(out, alignment_size)) {
+ ReportWriteFailure("code alignment padding", it);
+ return false;
+ }
+ offset_ += alignment_size;
+ DCHECK_OFFSET_();
+ }
+ DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ DCHECK_EQ(method_offsets.code_offset_,
+ offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
+ << dex_file_->PrettyMethod(it.GetMemberIndex());
+ const OatQuickMethodHeader& method_header =
+ oat_class->method_headers_[method_offsets_index_];
+ if (!out->WriteFully(&method_header, sizeof(method_header))) {
+ ReportWriteFailure("method header", it);
+ return false;
+ }
+ writer_->size_method_header_ += sizeof(method_header);
+ offset_ += sizeof(method_header);
+ DCHECK_OFFSET_();
+
+ if (!compiled_method->GetPatches().empty()) {
+ patched_code_.assign(quick_code.begin(), quick_code.end());
+ quick_code = ArrayRef<const uint8_t>(patched_code_);
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ uint32_t literal_offset = patch.LiteralOffset();
+ switch (patch.GetType()) {
+ case LinkerPatch::Type::kMethodBssEntry: {
+ uint32_t target_offset =
+ writer_->bss_start_ + writer_->bss_method_entries_.Get(patch.TargetMethod());
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kCallRelative: {
+ // NOTE: Relative calls across oat files are not supported.
+ uint32_t target_offset = GetTargetOffset(patch);
+ writer_->relative_patcher_->PatchCall(&patched_code_,
+ literal_offset,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kStringRelative: {
+ uint32_t target_offset = GetTargetObjectOffset(GetTargetString(patch));
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kStringInternTable: {
+ uint32_t target_offset = GetInternTableEntryOffset(patch);
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kStringBssEntry: {
+ StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex());
+ uint32_t target_offset =
+ writer_->bss_start_ + writer_->bss_string_entries_.Get(ref);
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kTypeRelative: {
+ uint32_t target_offset = GetTargetObjectOffset(GetTargetType(patch));
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kTypeClassTable: {
+ uint32_t target_offset = GetClassTableEntryOffset(patch);
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kTypeBssEntry: {
+ TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+ uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(ref);
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kCall: {
+ uint32_t target_offset = GetTargetOffset(patch);
+ PatchCodeAddress(&patched_code_, literal_offset, target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kMethodRelative: {
+ uint32_t target_offset = GetTargetMethodOffset(GetTargetMethod(patch));
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
+ case LinkerPatch::Type::kBakerReadBarrierBranch: {
+ writer_->relative_patcher_->PatchBakerReadBarrierBranch(&patched_code_,
+ patch,
+ offset_ + literal_offset);
+ break;
+ }
+ default: {
+ DCHECK(false) << "Unexpected linker patch type: " << patch.GetType();
+ break;
+ }
+ }
+ }
+ }
+
+ if (!out->WriteFully(quick_code.data(), code_size)) {
+ ReportWriteFailure("method code", it);
+ return false;
+ }
+ writer_->size_code_ += code_size;
+ offset_ += code_size;
+ }
+ DCHECK_OFFSET_();
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ const PointerSize pointer_size_;
+ ObjPtr<mirror::ClassLoader> class_loader_;
+ OutputStream* const out_;
+ const size_t file_offset_;
+ const ScopedObjectAccess soa_;
+ const ScopedAssertNoThreadSuspension no_thread_suspension_;
+ ClassLinker* const class_linker_;
+ ObjPtr<mirror::DexCache> dex_cache_;
+ std::vector<uint8_t> patched_code_;
+
+ void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) {
+ PLOG(ERROR) << "Failed to write " << what << " for "
+ << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation();
+ }
+
+ ArtMethod* GetTargetMethod(const LinkerPatch& patch)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ MethodReference ref = patch.TargetMethod();
+ ObjPtr<mirror::DexCache> dex_cache =
+ (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache(
+ Thread::Current(), *ref.dex_file);
+ ArtMethod* method = class_linker_->LookupResolvedMethod(ref.index, dex_cache, class_loader_);
+ CHECK(method != nullptr);
+ return method;
+ }
+
+ uint32_t GetTargetOffset(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t target_offset = writer_->relative_patcher_->GetOffset(patch.TargetMethod());
+ // If there's no new compiled code, either we're compiling an app and the target method
+ // is in the boot image, or we need to point to the correct trampoline.
+ if (UNLIKELY(target_offset == 0)) {
+ ArtMethod* target = GetTargetMethod(patch);
+ DCHECK(target != nullptr);
+ const void* oat_code_offset =
+ target->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+ if (oat_code_offset != 0) {
+ DCHECK(!writer_->HasBootImage());
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset));
+ target_offset = PointerToLowMemUInt32(oat_code_offset);
+ } else {
+ target_offset = target->IsNative()
+ ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+ : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+ }
+ }
+ return target_offset;
+ }
+
+ ObjPtr<mirror::DexCache> GetDexCache(const DexFile* target_dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (target_dex_file == dex_file_)
+ ? dex_cache_
+ : class_linker_->FindDexCache(Thread::Current(), *target_dex_file);
+ }
+
+ mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(writer_->HasImage());
+ ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile());
+ ObjPtr<mirror::Class> type =
+ ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_);
+ CHECK(type != nullptr);
+ return type.Ptr();
+ }
+
+ mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ mirror::String* string = linker->LookupString(*patch.TargetStringDexFile(),
+ patch.TargetStringIndex(),
+ GetDexCache(patch.TargetStringDexFile()));
+ DCHECK(string != nullptr);
+ DCHECK(writer_->HasBootImage() ||
+ Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string));
+ return string;
+ }
+
+ uint32_t GetTargetMethodOffset(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(writer_->HasBootImage());
+ method = writer_->image_writer_->GetImageMethodAddress(method);
+ size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_);
+ uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index);
+ // TODO: Clean up offset types. The target offset must be treated as signed.
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method) - oat_data_begin);
+ }
+
+ uint32_t GetTargetObjectOffset(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(writer_->HasBootImage());
+ object = writer_->image_writer_->GetImageAddress(object);
+ size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_);
+ uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index);
+ // TODO: Clean up offset types. The target offset must be treated as signed.
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object) - oat_data_begin);
+ }
+
+ void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (writer_->HasBootImage()) {
+ object = writer_->image_writer_->GetImageAddress(object);
+ } else {
+ // NOTE: We're using linker patches for app->boot references when the image can
+ // be relocated and therefore we need to emit .oat_patches. We're not using this
+ // for app->app references, so check that the object is in the image space.
+ DCHECK(Runtime::Current()->GetHeap()->FindSpaceFromObject(object, false)->IsImageSpace());
+ }
+ // Note: We only patch targeting Objects in image which is in the low 4gb.
+ uint32_t address = PointerToLowMemUInt32(object);
+ DCHECK_LE(offset + 4, code->size());
+ uint8_t* data = &(*code)[offset];
+ data[0] = address & 0xffu;
+ data[1] = (address >> 8) & 0xffu;
+ data[2] = (address >> 16) & 0xffu;
+ data[3] = (address >> 24) & 0xffu;
+ }
+
+ void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t address = target_offset;
+ if (writer_->HasBootImage()) {
+ size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_);
+ // TODO: Clean up offset types.
+ // The target_offset must be treated as signed for cross-oat patching.
+ const void* target = reinterpret_cast<const void*>(
+ writer_->image_writer_->GetOatDataBegin(oat_index) +
+ static_cast<int32_t>(target_offset));
+ address = PointerToLowMemUInt32(target);
+ }
+ DCHECK_LE(offset + 4, code->size());
+ uint8_t* data = &(*code)[offset];
+ data[0] = address & 0xffu;
+ data[1] = (address >> 8) & 0xffu;
+ data[2] = (address >> 16) & 0xffu;
+ data[3] = (address >> 24) & 0xffu;
+ }
+
+ // Calculate the offset of the InternTable slot (GcRoot<String>) when mmapped to the .bss.
+ uint32_t GetInternTableEntryOffset(const LinkerPatch& patch)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!writer_->HasBootImage());
+ const uint8_t* string_root = writer_->LookupBootImageInternTableSlot(
+ *patch.TargetStringDexFile(), patch.TargetStringIndex());
+ DCHECK(string_root != nullptr);
+ return GetBootImageTableEntryOffset(string_root);
+ }
+
+ // Calculate the offset of the ClassTable::TableSlot when mmapped to the .bss.
+ uint32_t GetClassTableEntryOffset(const LinkerPatch& patch)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!writer_->HasBootImage());
+ const uint8_t* table_slot =
+ writer_->LookupBootImageClassTableSlot(*patch.TargetTypeDexFile(), patch.TargetTypeIndex());
+ DCHECK(table_slot != nullptr);
+ return GetBootImageTableEntryOffset(table_slot);
+ }
+
+ uint32_t GetBootImageTableEntryOffset(const uint8_t* raw_root) {
+ uint32_t base_offset = writer_->bss_start_;
+ for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+ const uint8_t* const_tables_begin =
+ space->Begin() + space->GetImageHeader().GetBootImageConstantTablesOffset();
+ size_t offset = static_cast<size_t>(raw_root - const_tables_begin);
+ if (offset < space->GetImageHeader().GetBootImageConstantTablesSize()) {
+ DCHECK_LE(base_offset + offset, writer_->bss_start_ + writer_->bss_methods_offset_);
+ return base_offset + offset;
+ }
+ base_offset += space->GetImageHeader().GetBootImageConstantTablesSize();
+ }
+ LOG(FATAL) << "Didn't find boot image string in boot image intern tables!";
+ UNREACHABLE();
+ }
+};
+
+class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
+ public:
+ WriteMapMethodVisitor(OatWriter* writer,
+ OutputStream* out,
+ const size_t file_offset,
+ size_t relative_offset)
+ : OatDexMethodVisitor(writer, relative_offset),
+ out_(out),
+ file_offset_(file_offset) {}
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE {
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (HasCompiledCode(compiled_method)) {
+ size_t file_offset = file_offset_;
+ OutputStream* out = out_;
+
+ uint32_t map_offset = oat_class->method_headers_[method_offsets_index_].GetVmapTableOffset();
+ uint32_t code_offset = oat_class->method_offsets_[method_offsets_index_].code_offset_;
+ ++method_offsets_index_;
+
+ DCHECK((compiled_method->GetVmapTable().size() == 0u && map_offset == 0u) ||
+ (compiled_method->GetVmapTable().size() != 0u && map_offset != 0u))
+ << compiled_method->GetVmapTable().size() << " " << map_offset << " "
+ << dex_file_->PrettyMethod(it.GetMemberIndex());
+
+ // If vdex is enabled, only emit the map for compiled code. The quickening info
+ // is emitted in the vdex already.
+ if (map_offset != 0u) {
+ // Transform map_offset to actual oat data offset.
+ map_offset = (code_offset - compiled_method->CodeDelta()) - map_offset;
+ DCHECK_NE(map_offset, 0u);
+ DCHECK_LE(map_offset, offset_) << dex_file_->PrettyMethod(it.GetMemberIndex());
+
+ ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
+ size_t map_size = map.size() * sizeof(map[0]);
+ if (map_offset == offset_) {
+ // Write deduplicated map (code info for Optimizing or transformation info for dex2dex).
+ if (UNLIKELY(!out->WriteFully(map.data(), map_size))) {
+ ReportWriteFailure(it);
+ return false;
+ }
+ offset_ += map_size;
+ }
+ }
+ DCHECK_OFFSET_();
+ }
+
+ return true;
+ }
+
+ private:
+ OutputStream* const out_;
+ size_t const file_offset_;
+
+ void ReportWriteFailure(const ClassDataItemIterator& it) {
+ PLOG(ERROR) << "Failed to write map for "
+ << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation();
+ }
+};
+
+class OatWriter::WriteMethodInfoVisitor : public OatDexMethodVisitor {
+ public:
+ WriteMethodInfoVisitor(OatWriter* writer,
+ OutputStream* out,
+ const size_t file_offset,
+ size_t relative_offset)
+ : OatDexMethodVisitor(writer, relative_offset),
+ out_(out),
+ file_offset_(file_offset) {}
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE {
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (HasCompiledCode(compiled_method)) {
+ size_t file_offset = file_offset_;
+ OutputStream* out = out_;
+ uint32_t map_offset = oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset();
+ uint32_t code_offset = oat_class->method_offsets_[method_offsets_index_].code_offset_;
+ ++method_offsets_index_;
+ DCHECK((compiled_method->GetMethodInfo().size() == 0u && map_offset == 0u) ||
+ (compiled_method->GetMethodInfo().size() != 0u && map_offset != 0u))
+ << compiled_method->GetMethodInfo().size() << " " << map_offset << " "
+ << dex_file_->PrettyMethod(it.GetMemberIndex());
+ if (map_offset != 0u) {
+ // Transform map_offset to actual oat data offset.
+ map_offset = (code_offset - compiled_method->CodeDelta()) - map_offset;
+ DCHECK_NE(map_offset, 0u);
+ DCHECK_LE(map_offset, offset_) << dex_file_->PrettyMethod(it.GetMemberIndex());
+
+ ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo();
+ size_t map_size = map.size() * sizeof(map[0]);
+ if (map_offset == offset_) {
+ // Write deduplicated map (code info for Optimizing or transformation info for dex2dex).
+ if (UNLIKELY(!out->WriteFully(map.data(), map_size))) {
+ ReportWriteFailure(it);
+ return false;
+ }
+ offset_ += map_size;
+ }
+ }
+ DCHECK_OFFSET_();
+ }
+
+ return true;
+ }
+
+ private:
+ OutputStream* const out_;
+ size_t const file_offset_;
+
+ void ReportWriteFailure(const ClassDataItemIterator& it) {
+ PLOG(ERROR) << "Failed to write map for "
+ << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation();
+ }
+};
+
+// Visit all methods from all classes in all dex files with the specified visitor.
+bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) {
+ for (const DexFile* dex_file : *dex_files_) {
+ const size_t class_def_count = dex_file->NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) {
+ if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
+ return false;
+ }
+ if (MayHaveCompiledMethods()) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data != nullptr) { // ie not an empty class, such as a marker interface
+ ClassDataItemIterator it(*dex_file, class_data);
+ it.SkipAllFields();
+ size_t class_def_method_index = 0u;
+ while (it.HasNextDirectMethod()) {
+ if (!visitor->VisitMethod(class_def_method_index, it)) {
+ return false;
+ }
+ ++class_def_method_index;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
+ return false;
+ }
+ ++class_def_method_index;
+ it.Next();
+ }
+ }
+ }
+ if (UNLIKELY(!visitor->EndClass())) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+size_t OatWriter::InitOatHeader(InstructionSet instruction_set,
+ const InstructionSetFeatures* instruction_set_features,
+ uint32_t num_dex_files,
+ SafeMap<std::string, std::string>* key_value_store) {
+ TimingLogger::ScopedTiming split("InitOatHeader", timings_);
+ oat_header_.reset(OatHeader::Create(instruction_set,
+ instruction_set_features,
+ num_dex_files,
+ key_value_store));
+ size_oat_header_ += sizeof(OatHeader);
+ size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader);
+ return oat_header_->GetHeaderSize();
+}
+
+size_t OatWriter::InitClassOffsets(size_t offset) {
+ // Reserve space for class offsets in OAT and update class_offsets_offset_.
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ DCHECK_EQ(oat_dex_file.class_offsets_offset_, 0u);
+ if (!oat_dex_file.class_offsets_.empty()) {
+ // Class offsets are required to be 4 byte aligned.
+ offset = RoundUp(offset, 4u);
+ oat_dex_file.class_offsets_offset_ = offset;
+ offset += oat_dex_file.GetClassOffsetsRawSize();
+ DCHECK_ALIGNED(offset, 4u);
+ }
+ }
+ return offset;
+}
+
+size_t OatWriter::InitOatClasses(size_t offset) {
+ // calculate the offsets within OatDexFiles to OatClasses
+ InitOatClassesMethodVisitor visitor(this, offset);
+ bool success = VisitDexMethods(&visitor);
+ CHECK(success);
+ offset = visitor.GetOffset();
+
+ // Update oat_dex_files_.
+ auto oat_class_it = oat_class_headers_.begin();
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ for (uint32_t& class_offset : oat_dex_file.class_offsets_) {
+ DCHECK(oat_class_it != oat_class_headers_.end());
+ class_offset = oat_class_it->offset_;
+ ++oat_class_it;
+ }
+ }
+ CHECK(oat_class_it == oat_class_headers_.end());
+
+ return offset;
+}
+
+size_t OatWriter::InitOatMaps(size_t offset) {
+ if (!MayHaveCompiledMethods()) {
+ return offset;
+ }
+ {
+ InitMapMethodVisitor visitor(this, offset);
+ bool success = VisitDexMethods(&visitor);
+ DCHECK(success);
+ offset = visitor.GetOffset();
+ }
+ {
+ InitMethodInfoVisitor visitor(this, offset);
+ bool success = VisitDexMethods(&visitor);
+ DCHECK(success);
+ offset = visitor.GetOffset();
+ }
+ return offset;
+}
+
+size_t OatWriter::InitMethodBssMappings(size_t offset) {
+ size_t number_of_dex_files = 0u;
+ for (size_t i = 0, size = dex_files_->size(); i != size; ++i) {
+ const DexFile* dex_file = (*dex_files_)[i];
+ auto it = bss_method_entry_references_.find(dex_file);
+ if (it != bss_method_entry_references_.end()) {
+ const BitVector& method_indexes = it->second;
+ ++number_of_dex_files;
+ // If there are any classes, the class offsets allocation aligns the offset
+ // and we cannot have method bss mappings without class offsets.
+ static_assert(alignof(MethodBssMapping) == 4u, "MethodBssMapping alignment check.");
+ DCHECK_ALIGNED(offset, 4u);
+ oat_dex_files_[i].method_bss_mapping_offset_ = offset;
+
+ linker::MethodBssMappingEncoder encoder(
+ GetInstructionSetPointerSize(oat_header_->GetInstructionSet()));
+ size_t number_of_entries = 0u;
+ bool first_index = true;
+ for (uint32_t method_index : method_indexes.Indexes()) {
+ uint32_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index));
+ if (first_index || !encoder.TryMerge(method_index, bss_offset)) {
+ encoder.Reset(method_index, bss_offset);
+ ++number_of_entries;
+ first_index = false;
+ }
+ }
+ DCHECK_NE(number_of_entries, 0u);
+ offset += MethodBssMapping::ComputeSize(number_of_entries);
+ }
+ }
+ // Check that all dex files targeted by method bss entries are in `*dex_files_`.
+ CHECK_EQ(number_of_dex_files, bss_method_entry_references_.size());
+ return offset;
+}
+
+size_t OatWriter::InitOatDexFiles(size_t offset) {
+ // Initialize offsets of oat dex files.
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ oat_dex_file.offset_ = offset;
+ offset += oat_dex_file.SizeOf();
+ }
+ return offset;
+}
+
+size_t OatWriter::InitOatCode(size_t offset) {
+ // calculate the offsets within OatHeader to executable code
+ size_t old_offset = offset;
+ // required to be on a new page boundary
+ offset = RoundUp(offset, kPageSize);
+ oat_header_->SetExecutableOffset(offset);
+ size_executable_offset_alignment_ = offset - old_offset;
+ // TODO: Remove unused trampoline offsets from the OatHeader (requires oat version change).
+ oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
+ oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
+ if (compiler_driver_->GetCompilerOptions().IsBootImage()) {
+ InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+ const bool generate_debug_info = compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo();
+ size_t adjusted_offset = offset;
+
+ #define DO_TRAMPOLINE(field, fn_name) \
+ offset = CompiledCode::AlignCode(offset, instruction_set); \
+ adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \
+ oat_header_->Set ## fn_name ## Offset(adjusted_offset); \
+ (field) = compiler_driver_->Create ## fn_name(); \
+ if (generate_debug_info) { \
+ debug::MethodDebugInfo info = {}; \
+ info.trampoline_name = #fn_name; \
+ info.isa = instruction_set; \
+ info.is_code_address_text_relative = true; \
+ /* Use the code offset rather than the `adjusted_offset`. */ \
+ info.code_address = offset - oat_header_->GetExecutableOffset(); \
+ info.code_size = (field)->size(); \
+ method_info_.push_back(std::move(info)); \
+ } \
+ offset += (field)->size();
+
+ DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
+ DO_TRAMPOLINE(quick_generic_jni_trampoline_, QuickGenericJniTrampoline);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
+ DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
+ DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge);
+
+ #undef DO_TRAMPOLINE
+ } else {
+ oat_header_->SetJniDlsymLookupOffset(0);
+ oat_header_->SetQuickGenericJniTrampolineOffset(0);
+ oat_header_->SetQuickImtConflictTrampolineOffset(0);
+ oat_header_->SetQuickResolutionTrampolineOffset(0);
+ oat_header_->SetQuickToInterpreterBridgeOffset(0);
+ }
+ return offset;
+}
+
+size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
+ if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+ return offset;
+ }
+ InitCodeMethodVisitor code_visitor(this, offset);
+ bool success = VisitDexMethods(&code_visitor);
+ DCHECK(success);
+ offset = code_visitor.GetOffset();
+
+ if (HasImage()) {
+ InitImageMethodVisitor image_visitor(this, offset, dex_files_);
+ success = VisitDexMethods(&image_visitor);
+ image_visitor.Postprocess();
+ DCHECK(success);
+ offset = image_visitor.GetOffset();
+ }
+
+ return offset;
+}
+
+void OatWriter::InitBssLayout(InstructionSet instruction_set) {
+ {
+ InitBssLayoutMethodVisitor visitor(this);
+ bool success = VisitDexMethods(&visitor);
+ DCHECK(success);
+ }
+
+ DCHECK_EQ(bss_size_, 0u);
+ if (HasBootImage()) {
+ DCHECK(!map_boot_image_tables_to_bss_);
+ DCHECK(bss_string_entries_.empty());
+ }
+ if (!map_boot_image_tables_to_bss_ &&
+ bss_method_entries_.empty() &&
+ bss_type_entries_.empty() &&
+ bss_string_entries_.empty()) {
+ // Nothing to put to the .bss section.
+ return;
+ }
+
+ // Allocate space for boot image tables in the .bss section.
+ PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set);
+ if (map_boot_image_tables_to_bss_) {
+ for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+ bss_size_ += space->GetImageHeader().GetBootImageConstantTablesSize();
+ }
+ }
+
+ bss_methods_offset_ = bss_size_;
+
+ // Prepare offsets for .bss ArtMethod entries.
+ for (auto& entry : bss_method_entries_) {
+ DCHECK_EQ(entry.second, 0u);
+ entry.second = bss_size_;
+ bss_size_ += static_cast<size_t>(pointer_size);
+ }
+
+ bss_roots_offset_ = bss_size_;
+
+ // Prepare offsets for .bss Class entries.
+ for (auto& entry : bss_type_entries_) {
+ DCHECK_EQ(entry.second, 0u);
+ entry.second = bss_size_;
+ bss_size_ += sizeof(GcRoot<mirror::Class>);
+ }
+ // Prepare offsets for .bss String entries.
+ for (auto& entry : bss_string_entries_) {
+ DCHECK_EQ(entry.second, 0u);
+ entry.second = bss_size_;
+ bss_size_ += sizeof(GcRoot<mirror::String>);
+ }
+}
+
+bool OatWriter::WriteRodata(OutputStream* out) {
+ CHECK(write_state_ == WriteState::kWriteRoData);
+
+ size_t file_offset = oat_data_offset_;
+ off_t current_offset = out->Seek(0, kSeekCurrent);
+ if (current_offset == static_cast<off_t>(-1)) {
+ PLOG(ERROR) << "Failed to retrieve current position in " << out->GetLocation();
+ }
+ DCHECK_GE(static_cast<size_t>(current_offset), file_offset + oat_header_->GetHeaderSize());
+ size_t relative_offset = current_offset - file_offset;
+
+ // Wrap out to update checksum with each write.
+ ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+ out = &checksum_updating_out;
+
+ relative_offset = WriteClassOffsets(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ PLOG(ERROR) << "Failed to write class offsets to " << out->GetLocation();
+ return false;
+ }
+
+ relative_offset = WriteClasses(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ PLOG(ERROR) << "Failed to write classes to " << out->GetLocation();
+ return false;
+ }
+
+ relative_offset = WriteMethodBssMappings(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ PLOG(ERROR) << "Failed to write method bss mappings to " << out->GetLocation();
+ return false;
+ }
+
+ relative_offset = WriteMaps(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ PLOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
+ return false;
+ }
+
+ relative_offset = WriteOatDexFiles(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation();
+ return false;
+ }
+
+ // Write padding.
+ off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent);
+ relative_offset += size_executable_offset_alignment_;
+ DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset());
+ size_t expected_file_offset = file_offset + relative_offset;
+ if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
+ PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
+ << " Expected: " << expected_file_offset << " File: " << out->GetLocation();
+ return 0;
+ }
+ DCHECK_OFFSET();
+
+ write_state_ = WriteState::kWriteText;
+ return true;
+}
+
+class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor {
+ public:
+ WriteQuickeningInfoMethodVisitor(OatWriter* writer,
+ OutputStream* out,
+ uint32_t offset,
+ SafeMap<const uint8_t*, uint32_t>* offset_map)
+ : DexMethodVisitor(writer, offset),
+ out_(out),
+ written_bytes_(0u),
+ offset_map_(offset_map) {}
+
+ bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it)
+ OVERRIDE {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx));
+
+ if (HasQuickeningInfo(compiled_method)) {
+ ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
+ // Deduplication is already done on a pointer basis by the compiler driver,
+ // so we can simply compare the pointers to find out if things are duplicated.
+ if (offset_map_->find(map.data()) == offset_map_->end()) {
+ uint32_t length = map.size() * sizeof(map.front());
+ offset_map_->Put(map.data(), written_bytes_);
+ if (!out_->WriteFully(&length, sizeof(length)) ||
+ !out_->WriteFully(map.data(), length)) {
+ PLOG(ERROR) << "Failed to write quickening info for "
+ << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to "
+ << out_->GetLocation();
+ return false;
+ }
+ written_bytes_ += sizeof(length) + length;
+ offset_ += sizeof(length) + length;
+ }
+ }
+
+ return true;
+ }
+
+ size_t GetNumberOfWrittenBytes() const {
+ return written_bytes_;
+ }
+
+ private:
+ OutputStream* const out_;
+ size_t written_bytes_;
+ // Maps quickening map to its offset in the file.
+ SafeMap<const uint8_t*, uint32_t>* offset_map_;
+};
+
+class OatWriter::WriteQuickeningIndicesMethodVisitor {
+ public:
+ WriteQuickeningIndicesMethodVisitor(OutputStream* out,
+ uint32_t indices_offset,
+ const SafeMap<const uint8_t*, uint32_t>& offset_map,
+ std::vector<uint32_t>* dex_files_offset)
+ : out_(out),
+ indices_offset_(indices_offset),
+ written_bytes_(0u),
+ dex_files_offset_(dex_files_offset),
+ offset_map_(offset_map) {}
+
+ bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) {
+ for (const DexFile* dex_file : dex_files) {
+ // Record the offset for this current dex file. It will be written in the vdex file
+ // later.
+ dex_files_offset_->push_back(indices_offset_ + GetNumberOfWrittenBytes());
+ const size_t class_def_count = dex_file->NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ continue;
+ }
+ for (ClassDataItemIterator class_it(*dex_file, class_data);
+ class_it.HasNext();
+ class_it.Next()) {
+ if (!class_it.IsAtMethod()) {
+ continue;
+ }
+ uint32_t method_idx = class_it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ driver.GetCompiledMethod(MethodReference(dex_file, method_idx));
+ if (HasQuickeningInfo(compiled_method)) {
+ uint32_t code_item_offset = class_it.GetMethodCodeItemOffset();
+ uint32_t offset = offset_map_.Get(compiled_method->GetVmapTable().data());
+ if (!out_->WriteFully(&code_item_offset, sizeof(code_item_offset)) ||
+ !out_->WriteFully(&offset, sizeof(offset))) {
+ PLOG(ERROR) << "Failed to write quickening info for "
+ << dex_file->PrettyMethod(method_idx) << " to "
+ << out_->GetLocation();
+ return false;
+ }
+ written_bytes_ += sizeof(code_item_offset) + sizeof(offset);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ size_t GetNumberOfWrittenBytes() const {
+ return written_bytes_;
+ }
+
+ private:
+ OutputStream* const out_;
+ const uint32_t indices_offset_;
+ size_t written_bytes_;
+ std::vector<uint32_t>* dex_files_offset_;
+ // Maps quickening map to its offset in the file.
+ const SafeMap<const uint8_t*, uint32_t>& offset_map_;
+};
+
+bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) {
+ if (!kIsVdexEnabled) {
+ return true;
+ }
+
+ size_t initial_offset = vdex_size_;
+ size_t start_offset = RoundUp(initial_offset, 4u);
+
+ vdex_size_ = start_offset;
+ vdex_quickening_info_offset_ = vdex_size_;
+ size_quickening_info_alignment_ = start_offset - initial_offset;
+
+ off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet);
+ if (actual_offset != static_cast<off_t>(start_offset)) {
+ PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset
+ << " Expected: " << start_offset
+ << " Output: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+ std::vector<uint32_t> dex_files_indices;
+ SafeMap<const uint8_t*, uint32_t> offset_map;
+ WriteQuickeningInfoMethodVisitor visitor1(this, vdex_out, start_offset, &offset_map);
+ if (!VisitDexMethods(&visitor1)) {
+ PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ WriteQuickeningIndicesMethodVisitor visitor2(vdex_out,
+ visitor1.GetNumberOfWrittenBytes(),
+ offset_map,
+ &dex_files_indices);
+ if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) {
+ PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ DCHECK_EQ(dex_files_->size(), dex_files_indices.size());
+ if (!vdex_out->WriteFully(
+ dex_files_indices.data(), sizeof(dex_files_indices[0]) * dex_files_indices.size())) {
+ PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ if (!vdex_out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing quickening info."
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+ size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() +
+ visitor2.GetNumberOfWrittenBytes() +
+ dex_files_->size() * sizeof(uint32_t);
+ } else {
+ // We know we did not quicken.
+ size_quickening_info_ = 0;
+ }
+
+ vdex_size_ += size_quickening_info_;
+ return true;
+}
+
+bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps) {
+ if (!kIsVdexEnabled) {
+ return true;
+ }
+
+ if (verifier_deps == nullptr) {
+ // Nothing to write. Record the offset, but no need
+ // for alignment.
+ vdex_verifier_deps_offset_ = vdex_size_;
+ return true;
+ }
+
+ size_t initial_offset = vdex_size_;
+ size_t start_offset = RoundUp(initial_offset, 4u);
+
+ vdex_size_ = start_offset;
+ vdex_verifier_deps_offset_ = vdex_size_;
+ size_verifier_deps_alignment_ = start_offset - initial_offset;
+
+ off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet);
+ if (actual_offset != static_cast<off_t>(start_offset)) {
+ PLOG(ERROR) << "Failed to seek to verifier deps section. Actual: " << actual_offset
+ << " Expected: " << start_offset
+ << " Output: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ std::vector<uint8_t> buffer;
+ verifier_deps->Encode(*dex_files_, &buffer);
+
+ if (!vdex_out->WriteFully(buffer.data(), buffer.size())) {
+ PLOG(ERROR) << "Failed to write verifier deps."
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+ if (!vdex_out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing verifier deps."
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ size_verifier_deps_ = buffer.size();
+ vdex_size_ += size_verifier_deps_;
+ return true;
+}
+
+bool OatWriter::WriteCode(OutputStream* out) {
+ CHECK(write_state_ == WriteState::kWriteText);
+
+ // Wrap out to update checksum with each write.
+ ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+ out = &checksum_updating_out;
+
+ SetMultiOatRelativePatcherAdjustment();
+
+ const size_t file_offset = oat_data_offset_;
+ size_t relative_offset = oat_header_->GetExecutableOffset();
+ DCHECK_OFFSET();
+
+ relative_offset = WriteCode(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
+ return false;
+ }
+
+ relative_offset = WriteCodeDexFiles(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ LOG(ERROR) << "Failed to write oat code for dex files to " << out->GetLocation();
+ return false;
+ }
+
+ const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent);
+ if (oat_end_file_offset == static_cast<off_t>(-1)) {
+ LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation();
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ uint32_t size_total = 0;
+ #define DO_STAT(x) \
+ VLOG(compiler) << #x "=" << PrettySize(x) << " (" << (x) << "B)"; \
+ size_total += (x);
+
+ DO_STAT(size_vdex_header_);
+ DO_STAT(size_vdex_checksums_);
+ DO_STAT(size_dex_file_alignment_);
+ DO_STAT(size_executable_offset_alignment_);
+ DO_STAT(size_oat_header_);
+ DO_STAT(size_oat_header_key_value_store_);
+ DO_STAT(size_dex_file_);
+ DO_STAT(size_verifier_deps_);
+ DO_STAT(size_verifier_deps_alignment_);
+ DO_STAT(size_quickening_info_);
+ DO_STAT(size_quickening_info_alignment_);
+ DO_STAT(size_interpreter_to_interpreter_bridge_);
+ DO_STAT(size_interpreter_to_compiled_code_bridge_);
+ DO_STAT(size_jni_dlsym_lookup_);
+ DO_STAT(size_quick_generic_jni_trampoline_);
+ DO_STAT(size_quick_imt_conflict_trampoline_);
+ DO_STAT(size_quick_resolution_trampoline_);
+ DO_STAT(size_quick_to_interpreter_bridge_);
+ DO_STAT(size_trampoline_alignment_);
+ DO_STAT(size_method_header_);
+ DO_STAT(size_code_);
+ DO_STAT(size_code_alignment_);
+ DO_STAT(size_relative_call_thunks_);
+ DO_STAT(size_misc_thunks_);
+ DO_STAT(size_vmap_table_);
+ DO_STAT(size_method_info_);
+ DO_STAT(size_oat_dex_file_location_size_);
+ DO_STAT(size_oat_dex_file_location_data_);
+ DO_STAT(size_oat_dex_file_location_checksum_);
+ DO_STAT(size_oat_dex_file_offset_);
+ DO_STAT(size_oat_dex_file_class_offsets_offset_);
+ DO_STAT(size_oat_dex_file_lookup_table_offset_);
+ DO_STAT(size_oat_dex_file_dex_layout_sections_offset_);
+ DO_STAT(size_oat_dex_file_dex_layout_sections_);
+ DO_STAT(size_oat_dex_file_dex_layout_sections_alignment_);
+ DO_STAT(size_oat_dex_file_method_bss_mapping_offset_);
+ DO_STAT(size_oat_lookup_table_alignment_);
+ DO_STAT(size_oat_lookup_table_);
+ DO_STAT(size_oat_class_offsets_alignment_);
+ DO_STAT(size_oat_class_offsets_);
+ DO_STAT(size_oat_class_type_);
+ DO_STAT(size_oat_class_status_);
+ DO_STAT(size_oat_class_method_bitmaps_);
+ DO_STAT(size_oat_class_method_offsets_);
+ DO_STAT(size_method_bss_mappings_);
+ #undef DO_STAT
+
+ 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 + oat_size_, static_cast<size_t>(oat_end_file_offset));
+ CHECK_EQ(oat_size_, relative_offset);
+
+ write_state_ = WriteState::kWriteHeader;
+ return true;
+}
+
+bool OatWriter::WriteHeader(OutputStream* out,
+ uint32_t image_file_location_oat_checksum,
+ uintptr_t image_file_location_oat_begin,
+ int32_t image_patch_delta) {
+ CHECK(write_state_ == WriteState::kWriteHeader);
+
+ oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum);
+ oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin);
+ if (compiler_driver_->GetCompilerOptions().IsBootImage()) {
+ CHECK_EQ(image_patch_delta, 0);
+ CHECK_EQ(oat_header_->GetImagePatchDelta(), 0);
+ } else {
+ CHECK_ALIGNED(image_patch_delta, kPageSize);
+ oat_header_->SetImagePatchDelta(image_patch_delta);
+ }
+ oat_header_->UpdateChecksumWithHeaderData();
+
+ const size_t file_offset = oat_data_offset_;
+
+ off_t current_offset = out->Seek(0, kSeekCurrent);
+ if (current_offset == static_cast<off_t>(-1)) {
+ PLOG(ERROR) << "Failed to get current offset from " << out->GetLocation();
+ return false;
+ }
+ if (out->Seek(file_offset, kSeekSet) == static_cast<off_t>(-1)) {
+ PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation();
+ return false;
+ }
+ DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent)));
+
+ // Flush all other data before writing the header.
+ if (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush before writing oat header to " << out->GetLocation();
+ return false;
+ }
+ // Write the header.
+ size_t header_size = oat_header_->GetHeaderSize();
+ if (!out->WriteFully(oat_header_.get(), header_size)) {
+ PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
+ return false;
+ }
+ // Flush the header data.
+ if (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush after writing oat header to " << out->GetLocation();
+ return false;
+ }
+
+ if (out->Seek(current_offset, kSeekSet) == static_cast<off_t>(-1)) {
+ PLOG(ERROR) << "Failed to seek back after writing oat header to " << out->GetLocation();
+ return false;
+ }
+ DCHECK_EQ(current_offset, out->Seek(0, kSeekCurrent));
+
+ write_state_ = WriteState::kDone;
+ return true;
+}
+
+size_t OatWriter::WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset) {
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ if (oat_dex_file.class_offsets_offset_ != 0u) {
+ // Class offsets are required to be 4 byte aligned.
+ if (UNLIKELY(!IsAligned<4u>(relative_offset))) {
+ size_t padding_size = RoundUp(relative_offset, 4u) - relative_offset;
+ if (!WriteUpTo16BytesAlignment(out, padding_size, &size_oat_class_offsets_alignment_)) {
+ return 0u;
+ }
+ relative_offset += padding_size;
+ }
+ DCHECK_OFFSET();
+ if (!oat_dex_file.WriteClassOffsets(this, out)) {
+ return 0u;
+ }
+ relative_offset += oat_dex_file.GetClassOffsetsRawSize();
+ }
+ }
+ return relative_offset;
+}
+
+size_t OatWriter::WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset) {
+ const bool may_have_compiled = MayHaveCompiledMethods();
+ if (may_have_compiled) {
+ CHECK_EQ(oat_class_headers_.size(), oat_classes_.size());
+ }
+ for (size_t i = 0; i < oat_class_headers_.size(); ++i) {
+ // If there are any classes, the class offsets allocation aligns the offset.
+ DCHECK_ALIGNED(relative_offset, 4u);
+ DCHECK_OFFSET();
+ if (!oat_class_headers_[i].Write(this, out, oat_data_offset_)) {
+ return 0u;
+ }
+ relative_offset += oat_class_headers_[i].SizeOf();
+ if (may_have_compiled) {
+ if (!oat_classes_[i].Write(this, out)) {
+ return 0u;
+ }
+ relative_offset += oat_classes_[i].SizeOf();
+ }
+ }
+ return relative_offset;
+}
+
+size_t OatWriter::WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset) {
+ {
+ size_t vmap_tables_offset = relative_offset;
+ WriteMapMethodVisitor visitor(this, out, file_offset, relative_offset);
+ if (UNLIKELY(!VisitDexMethods(&visitor))) {
+ return 0;
+ }
+ relative_offset = visitor.GetOffset();
+ size_vmap_table_ = relative_offset - vmap_tables_offset;
+ }
+ {
+ size_t method_infos_offset = relative_offset;
+ WriteMethodInfoVisitor visitor(this, out, file_offset, relative_offset);
+ if (UNLIKELY(!VisitDexMethods(&visitor))) {
+ return 0;
+ }
+ relative_offset = visitor.GetOffset();
+ size_method_info_ = relative_offset - method_infos_offset;
+ }
+
+ return relative_offset;
+}
+
+size_t OatWriter::WriteMethodBssMappings(OutputStream* out,
+ size_t file_offset,
+ size_t relative_offset) {
+ TimingLogger::ScopedTiming split("WriteMethodBssMappings", timings_);
+
+ for (size_t i = 0, size = dex_files_->size(); i != size; ++i) {
+ const DexFile* dex_file = (*dex_files_)[i];
+ OatDexFile* oat_dex_file = &oat_dex_files_[i];
+ auto it = bss_method_entry_references_.find(dex_file);
+ if (it != bss_method_entry_references_.end()) {
+ const BitVector& method_indexes = it->second;
+ // If there are any classes, the class offsets allocation aligns the offset
+ // and we cannot have method bss mappings without class offsets.
+ static_assert(alignof(MethodBssMapping) == sizeof(uint32_t),
+ "MethodBssMapping alignment check.");
+ DCHECK_ALIGNED(relative_offset, sizeof(uint32_t));
+
+ linker::MethodBssMappingEncoder encoder(
+ GetInstructionSetPointerSize(oat_header_->GetInstructionSet()));
+ // Allocate a sufficiently large MethodBssMapping.
+ size_t number_of_method_indexes = method_indexes.NumSetBits();
+ DCHECK_NE(number_of_method_indexes, 0u);
+ size_t max_mappings_size = MethodBssMapping::ComputeSize(number_of_method_indexes);
+ DCHECK_ALIGNED(max_mappings_size, sizeof(uint32_t));
+ std::unique_ptr<uint32_t[]> storage(new uint32_t[max_mappings_size / sizeof(uint32_t)]);
+ MethodBssMapping* mappings = new(storage.get()) MethodBssMapping(number_of_method_indexes);
+ mappings->ClearPadding();
+ // Encode the MethodBssMapping.
+ auto init_it = mappings->begin();
+ bool first_index = true;
+ for (uint32_t method_index : method_indexes.Indexes()) {
+ size_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index));
+ if (first_index) {
+ first_index = false;
+ encoder.Reset(method_index, bss_offset);
+ } else if (!encoder.TryMerge(method_index, bss_offset)) {
+ *init_it = encoder.GetEntry();
+ ++init_it;
+ encoder.Reset(method_index, bss_offset);
+ }
+ }
+ // Store the last entry and shrink the mapping to the actual size.
+ *init_it = encoder.GetEntry();
+ ++init_it;
+ DCHECK(init_it <= mappings->end());
+ mappings->SetSize(std::distance(mappings->begin(), init_it));
+ size_t mappings_size = MethodBssMapping::ComputeSize(mappings->size());
+
+ DCHECK_EQ(relative_offset, oat_dex_file->method_bss_mapping_offset_);
+ DCHECK_OFFSET();
+ if (!out->WriteFully(storage.get(), mappings_size)) {
+ return 0u;
+ }
+ size_method_bss_mappings_ += mappings_size;
+ relative_offset += mappings_size;
+ } else {
+ DCHECK_EQ(0u, oat_dex_file->method_bss_mapping_offset_);
+ }
+ }
+ return relative_offset;
+}
+
+size_t OatWriter::WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset) {
+ TimingLogger::ScopedTiming split("WriteOatDexFiles", timings_);
+
+ for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) {
+ OatDexFile* oat_dex_file = &oat_dex_files_[i];
+ DCHECK_EQ(relative_offset, oat_dex_file->offset_);
+ DCHECK_OFFSET();
+
+ // Write OatDexFile.
+ if (!oat_dex_file->Write(this, out)) {
+ return 0u;
+ }
+ relative_offset += oat_dex_file->SizeOf();
+ }
+
+ return relative_offset;
+}
+
+size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset) {
+ if (compiler_driver_->GetCompilerOptions().IsBootImage()) {
+ InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+
+ #define DO_TRAMPOLINE(field) \
+ do { \
+ uint32_t aligned_offset = CompiledCode::AlignCode(relative_offset, instruction_set); \
+ uint32_t alignment_padding = aligned_offset - relative_offset; \
+ out->Seek(alignment_padding, kSeekCurrent); \
+ size_trampoline_alignment_ += alignment_padding; \
+ if (!out->WriteFully((field)->data(), (field)->size())) { \
+ PLOG(ERROR) << "Failed to write " # field " to " << out->GetLocation(); \
+ return false; \
+ } \
+ size_ ## field += (field)->size(); \
+ relative_offset += alignment_padding + (field)->size(); \
+ DCHECK_OFFSET(); \
+ } while (false)
+
+ DO_TRAMPOLINE(jni_dlsym_lookup_);
+ DO_TRAMPOLINE(quick_generic_jni_trampoline_);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
+ DO_TRAMPOLINE(quick_resolution_trampoline_);
+ DO_TRAMPOLINE(quick_to_interpreter_bridge_);
+ #undef DO_TRAMPOLINE
+ }
+ return relative_offset;
+}
+
+size_t OatWriter::WriteCodeDexFiles(OutputStream* out,
+ size_t file_offset,
+ size_t relative_offset) {
+ #define VISIT(VisitorType) \
+ do { \
+ VisitorType visitor(this, out, file_offset, relative_offset); \
+ if (UNLIKELY(!VisitDexMethods(&visitor))) { \
+ return 0; \
+ } \
+ relative_offset = visitor.GetOffset(); \
+ } while (false)
+
+ VISIT(WriteCodeMethodVisitor);
+
+ #undef VISIT
+
+ size_code_alignment_ += relative_patcher_->CodeAlignmentSize();
+ size_relative_call_thunks_ += relative_patcher_->RelativeCallThunksSize();
+ size_misc_thunks_ += relative_patcher_->MiscThunksSize();
+
+ return relative_offset;
+}
+
+bool OatWriter::RecordOatDataOffset(OutputStream* out) {
+ // Get the elf file offset of the oat file.
+ const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
+ if (raw_file_offset == static_cast<off_t>(-1)) {
+ LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+ return false;
+ }
+ oat_data_offset_ = static_cast<size_t>(raw_file_offset);
+ return true;
+}
+
+bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) {
+ // Read the dex file header and perform minimal verification.
+ uint8_t raw_header[sizeof(DexFile::Header)];
+ if (!file->ReadFully(&raw_header, sizeof(DexFile::Header))) {
+ PLOG(ERROR) << "Failed to read dex file header. Actual: "
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (!ValidateDexFileHeader(raw_header, oat_dex_file->GetLocation())) {
+ return false;
+ }
+
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header);
+ oat_dex_file->dex_file_size_ = header->file_size_;
+ oat_dex_file->dex_file_location_checksum_ = header->checksum_;
+ oat_dex_file->class_offsets_.resize(header->class_defs_size_);
+ return true;
+}
+
+bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) {
+ if (!DexFile::IsMagicValid(raw_header)) {
+ LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location;
+ return false;
+ }
+ if (!DexFile::IsVersionValid(raw_header)) {
+ LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location;
+ return false;
+ }
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header);
+ if (header->file_size_ < sizeof(DexFile::Header)) {
+ LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header."
+ << " File: " << location;
+ return false;
+ }
+ return true;
+}
+
+bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) {
+ TimingLogger::ScopedTiming split("Write Dex files", timings_);
+
+ vdex_dex_files_offset_ = vdex_size_;
+
+ // Write dex files.
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) {
+ return false;
+ }
+ }
+
+ CloseSources();
+ return true;
+}
+
+void OatWriter::CloseSources() {
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated.
+ }
+ zipped_dex_files_.clear();
+ zip_archives_.clear();
+ raw_dex_files_.clear();
+}
+
+bool OatWriter::WriteDexFile(OutputStream* out,
+ File* file,
+ OatDexFile* oat_dex_file,
+ bool update_input_vdex) {
+ if (!SeekToDexFile(out, file, oat_dex_file)) {
+ return false;
+ }
+ if (profile_compilation_info_ != nullptr) {
+ CHECK(!update_input_vdex) << "We should never update the input vdex when doing dexlayout";
+ if (!LayoutAndWriteDexFile(out, oat_dex_file)) {
+ return false;
+ }
+ } else if (oat_dex_file->source_.IsZipEntry()) {
+ DCHECK(!update_input_vdex);
+ if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
+ return false;
+ }
+ } else if (oat_dex_file->source_.IsRawFile()) {
+ DCHECK(!update_input_vdex);
+ if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
+ return false;
+ }
+ } else {
+ DCHECK(oat_dex_file->source_.IsRawData());
+ if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData(), update_input_vdex)) {
+ return false;
+ }
+ }
+
+ // Update current size and account for the written data.
+ if (kIsVdexEnabled) {
+ DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_);
+ vdex_size_ += oat_dex_file->dex_file_size_;
+ } else {
+ DCHECK(!update_input_vdex);
+ 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 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.
+ 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: " << file_offset
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush before writing dex file."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ actual_offset = lseek(file->Fd(), 0, SEEK_CUR);
+ if (actual_offset != static_cast<off_t>(file_offset)) {
+ PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset
+ << " Expected: " << file_offset
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+
+ if (kIsVdexEnabled) {
+ vdex_size_ = start_offset;
+ } else {
+ oat_size_ = start_offset;
+ }
+ oat_dex_file->dex_file_offset_ = start_offset;
+ return true;
+}
+
+bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) {
+ TimingLogger::ScopedTiming split("Dex Layout", timings_);
+ std::string error_msg;
+ std::string location(oat_dex_file->GetLocation());
+ std::unique_ptr<const DexFile> dex_file;
+ if (oat_dex_file->source_.IsZipEntry()) {
+ ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry();
+ std::unique_ptr<MemMap> mem_map(
+ zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg));
+ if (mem_map == nullptr) {
+ LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg;
+ return false;
+ }
+ dex_file = DexFile::Open(location,
+ zip_entry->GetCrc32(),
+ std::move(mem_map),
+ /* verify */ true,
+ /* verify_checksum */ true,
+ &error_msg);
+ } else if (oat_dex_file->source_.IsRawFile()) {
+ File* raw_file = oat_dex_file->source_.GetRawFile();
+ int dup_fd = dup(raw_file->Fd());
+ if (dup_fd < 0) {
+ PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location;
+ return false;
+ }
+ dex_file = DexFile::OpenDex(dup_fd, location, /* verify_checksum */ true, &error_msg);
+ } else {
+ // The source data is a vdex file.
+ CHECK(oat_dex_file->source_.IsRawData())
+ << static_cast<size_t>(oat_dex_file->source_.GetType());
+ const uint8_t* raw_dex_file = oat_dex_file->source_.GetRawData();
+ // Note: The raw data has already been checked to contain the header
+ // and all the data that the header specifies as the file size.
+ DCHECK(raw_dex_file != nullptr);
+ DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation()));
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
+ // Since the source may have had its layout changed, or may be quickened, don't verify it.
+ dex_file = DexFile::Open(raw_dex_file,
+ header->file_size_,
+ location,
+ oat_dex_file->dex_file_location_checksum_,
+ nullptr,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ &error_msg);
+ }
+ if (dex_file == nullptr) {
+ LOG(ERROR) << "Failed to open dex file for layout: " << error_msg;
+ return false;
+ }
+ Options options;
+ options.output_to_memmap_ = true;
+ DexLayout dex_layout(options, profile_compilation_info_, nullptr);
+ dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0);
+ std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap());
+ if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) {
+ return false;
+ }
+ oat_dex_file->dex_sections_layout_ = dex_layout.GetSections();
+ // Set the checksum of the new oat dex file to be the original file's checksum.
+ oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum();
+ return true;
+}
+
+bool OatWriter::WriteDexFile(OutputStream* out,
+ File* file,
+ OatDexFile* oat_dex_file,
+ ZipEntry* dex_file) {
+ 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;
+ if (!dex_file->ExtractToFile(*file, &error_msg)) {
+ LOG(ERROR) << "Failed to extract dex file from ZIP entry: " << error_msg
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (file->Flush() != 0) {
+ PLOG(ERROR) << "Failed to flush dex file from ZIP entry."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ off_t extracted_end = lseek(file->Fd(), 0, SEEK_CUR);
+ if (extracted_end == static_cast<off_t>(-1)) {
+ PLOG(ERROR) << "Failed get end offset after writing dex file from ZIP entry."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (extracted_end < static_cast<off_t>(start_offset)) {
+ LOG(ERROR) << "Dex file end position is before start position! End: " << extracted_end
+ << " Start: " << start_offset
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ uint64_t extracted_size = static_cast<uint64_t>(extracted_end - start_offset);
+ if (extracted_size < sizeof(DexFile::Header)) {
+ LOG(ERROR) << "Extracted dex file is shorter than dex file header. size: "
+ << extracted_size << " File: " << oat_dex_file->GetLocation();
+ return false;
+ }
+
+ // Read the dex file header and extract required data to OatDexFile.
+ off_t actual_offset = lseek(file->Fd(), start_offset, SEEK_SET);
+ if (actual_offset != static_cast<off_t>(start_offset)) {
+ PLOG(ERROR) << "Failed to seek back to dex file header. Actual: " << actual_offset
+ << " Expected: " << start_offset
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (!ReadDexFileHeader(file, oat_dex_file)) {
+ return false;
+ }
+ if (extracted_size < oat_dex_file->dex_file_size_) {
+ LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size
+ << " file size from header: " << oat_dex_file->dex_file_size_
+ << " File: " << oat_dex_file->GetLocation();
+ return false;
+ }
+
+ // Override the checksum from header with the CRC from ZIP entry.
+ oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32();
+
+ // Seek both file and stream to the end offset.
+ size_t end_offset = start_offset + oat_dex_file->dex_file_size_;
+ actual_offset = lseek(file->Fd(), end_offset, SEEK_SET);
+ if (actual_offset != static_cast<off_t>(end_offset)) {
+ PLOG(ERROR) << "Failed to seek to end of dex file. Actual: " << actual_offset
+ << " Expected: " << end_offset
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ 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 (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after seeking over dex file."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+
+ // If we extracted more than the size specified in the header, truncate the file.
+ 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();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool OatWriter::WriteDexFile(OutputStream* out,
+ File* file,
+ OatDexFile* oat_dex_file,
+ File* dex_file) {
+ 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)) {
+ PLOG(ERROR) << "Failed to seek to dex file header. Actual: " << input_offset
+ << " Expected: 0"
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (!ReadDexFileHeader(dex_file, oat_dex_file)) {
+ return false;
+ }
+
+ // Copy the input dex file using sendfile().
+ if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) {
+ PLOG(ERROR) << "Failed to copy dex file to oat file."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ if (file->Flush() != 0) {
+ PLOG(ERROR) << "Failed to flush dex file."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+
+ // Check file position and seek the stream to the end offset.
+ size_t end_offset = start_offset + oat_dex_file->dex_file_size_;
+ off_t actual_offset = lseek(file->Fd(), 0, SEEK_CUR);
+ if (actual_offset != static_cast<off_t>(end_offset)) {
+ PLOG(ERROR) << "Unexpected file position after copying dex file. Actual: " << actual_offset
+ << " Expected: " << end_offset
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+ 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 (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after seeking over dex file."
+ << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+ return false;
+ }
+
+ return true;
+}
+
+bool OatWriter::WriteDexFile(OutputStream* out,
+ OatDexFile* oat_dex_file,
+ const uint8_t* dex_file,
+ bool update_input_vdex) {
+ // Note: The raw data has already been checked to contain the header
+ // and all the data that the header specifies as the file size.
+ DCHECK(dex_file != nullptr);
+ DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation()));
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file);
+
+ if (update_input_vdex) {
+ // The vdex already contains the dex code, no need to write it again.
+ } else {
+ if (!out->WriteFully(dex_file, header->file_size_)) {
+ PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
+ << " to " << out->GetLocation();
+ return false;
+ }
+ if (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing dex file."
+ << " File: " << oat_dex_file->GetLocation();
+ return false;
+ }
+ }
+
+ // Update dex file size and resize class offsets in the OatDexFile.
+ // Note: For raw data, the checksum is passed directly to AddRawDexFileSource().
+ // Note: For vdex, the checksum is copied from the existing vdex file.
+ oat_dex_file->dex_file_size_ = header->file_size_;
+ oat_dex_file->class_offsets_.resize(header->class_defs_size_);
+ return true;
+}
+
+bool OatWriter::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) {
+ TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
+
+ if (oat_dex_files_.empty()) {
+ // Nothing to do.
+ return true;
+ }
+
+ size_t map_offset = oat_dex_files_[0].dex_file_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(),
+ 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;
+ return false;
+ }
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ // Make sure no one messed with input files while we were copying data.
+ // At the very least we need consistent file size and number of class definitions.
+ const uint8_t* raw_dex_file =
+ dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset;
+ if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) {
+ // Note: ValidateDexFileHeader() already logged an error message.
+ LOG(ERROR) << "Failed to verify written dex file header!"
+ << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset
+ << " ~ " << static_cast<const void*>(raw_dex_file);
+ return false;
+ }
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
+ if (header->file_size_ != oat_dex_file.dex_file_size_) {
+ LOG(ERROR) << "File size mismatch in written dex file header! Expected: "
+ << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_
+ << " Output: " << file->GetPath();
+ return false;
+ }
+ if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) {
+ LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: "
+ << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_
+ << " Output: " << file->GetPath();
+ return false;
+ }
+
+ // Now, open the dex file.
+ dex_files.emplace_back(DexFile::Open(raw_dex_file,
+ oat_dex_file.dex_file_size_,
+ oat_dex_file.GetLocation(),
+ oat_dex_file.dex_file_location_checksum_,
+ /* oat_dex_file */ nullptr,
+ verify,
+ verify,
+ &error_msg));
+ if (dex_files.back() == nullptr) {
+ LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
+ << " Error: " << error_msg;
+ return false;
+ }
+ }
+
+ *opened_dex_files_map = std::move(dex_files_map);
+ *opened_dex_files = std::move(dex_files);
+ return true;
+}
+
+bool OatWriter::WriteTypeLookupTables(
+ 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];
+ DCHECK_EQ(oat_dex_file->lookup_table_offset_, 0u);
+
+ if (oat_dex_file->create_type_lookup_table_ != CreateTypeLookupTable::kCreate ||
+ oat_dex_file->class_offsets_.empty()) {
+ continue;
+ }
+
+ size_t table_size = TypeLookupTable::RawDataLength(oat_dex_file->class_offsets_.size());
+ if (table_size == 0u) {
+ continue;
+ }
+
+ // Create the lookup table. When `nullptr` is given as the storage buffer,
+ // TypeLookupTable allocates its own and OatDexFile takes ownership.
+ const DexFile& dex_file = *opened_dex_files[i];
+ {
+ std::unique_ptr<TypeLookupTable> type_lookup_table =
+ TypeLookupTable::Create(dex_file, /* storage */ nullptr);
+ type_lookup_table_oat_dex_files_.push_back(
+ std::make_unique<art::OatDexFile>(std::move(type_lookup_table)));
+ dex_file.SetOatDexFile(type_lookup_table_oat_dex_files_.back().get());
+ }
+ TypeLookupTable* const table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable();
+
+ // Type tables are required to be 4 byte aligned.
+ 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 (!oat_rodata->WriteFully(buffer.data(), padding_size)) {
+ PLOG(ERROR) << "Failed to write lookup table alignment padding."
+ << " File: " << oat_dex_file->GetLocation()
+ << " Output: " << oat_rodata->GetLocation();
+ return false;
+ }
+ }
+
+ DCHECK_EQ(oat_data_offset_ + rodata_offset,
+ static_cast<size_t>(oat_rodata->Seek(0u, kSeekCurrent)));
+ DCHECK_EQ(table_size, table->RawDataLength());
+
+ if (!oat_rodata->WriteFully(table->RawData(), table_size)) {
+ PLOG(ERROR) << "Failed to write lookup table."
+ << " File: " << oat_dex_file->GetLocation()
+ << " Output: " << oat_rodata->GetLocation();
+ return false;
+ }
+
+ oat_dex_file->lookup_table_offset_ = rodata_offset;
+
+ oat_size_ += padding_size + table_size;
+ size_oat_lookup_table_ += table_size;
+ size_oat_lookup_table_alignment_ += padding_size;
+ }
+
+ if (!oat_rodata->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing type lookup tables."
+ << " File: " << oat_rodata->GetLocation();
+ return false;
+ }
+
+ return true;
+}
+
+bool OatWriter::WriteDexLayoutSections(
+ OutputStream* oat_rodata,
+ const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) {
+ TimingLogger::ScopedTiming split(__FUNCTION__, timings_);
+
+ if (!kWriteDexLayoutInfo) {
+ return true;;
+ }
+
+ 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 dex layout section offset section. Actual: " << actual_offset
+ << " Expected: " << expected_offset << " File: " << oat_rodata->GetLocation();
+ return false;
+ }
+
+ DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size());
+ size_t rodata_offset = oat_size_;
+ for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) {
+ OatDexFile* oat_dex_file = &oat_dex_files_[i];
+ DCHECK_EQ(oat_dex_file->dex_sections_layout_offset_, 0u);
+
+ // Write dex layout section alignment bytes.
+ const size_t padding_size =
+ RoundUp(rodata_offset, alignof(DexLayoutSections)) - rodata_offset;
+ if (padding_size != 0u) {
+ std::vector<uint8_t> buffer(padding_size, 0u);
+ if (!oat_rodata->WriteFully(buffer.data(), padding_size)) {
+ PLOG(ERROR) << "Failed to write lookup table alignment padding."
+ << " File: " << oat_dex_file->GetLocation()
+ << " Output: " << oat_rodata->GetLocation();
+ return false;
+ }
+ size_oat_dex_file_dex_layout_sections_alignment_ += padding_size;
+ rodata_offset += padding_size;
+ }
+
+ DCHECK_ALIGNED(rodata_offset, alignof(DexLayoutSections));
+ DCHECK_EQ(oat_data_offset_ + rodata_offset,
+ static_cast<size_t>(oat_rodata->Seek(0u, kSeekCurrent)));
+ DCHECK(oat_dex_file != nullptr);
+ if (!oat_rodata->WriteFully(&oat_dex_file->dex_sections_layout_,
+ sizeof(oat_dex_file->dex_sections_layout_))) {
+ PLOG(ERROR) << "Failed to write dex layout sections."
+ << " File: " << oat_dex_file->GetLocation()
+ << " Output: " << oat_rodata->GetLocation();
+ return false;
+ }
+ oat_dex_file->dex_sections_layout_offset_ = rodata_offset;
+ size_oat_dex_file_dex_layout_sections_ += sizeof(oat_dex_file->dex_sections_layout_);
+ rodata_offset += sizeof(oat_dex_file->dex_sections_layout_);
+ }
+ oat_size_ = rodata_offset;
+
+ if (!oat_rodata->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing type dex layout sections."
+ << " File: " << oat_rodata->GetLocation();
+ return false;
+ }
+
+ return true;
+}
+
+bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) {
+ if (!kIsVdexEnabled) {
+ return true;
+ }
+ // Write checksums
+ off_t actual_offset = vdex_out->Seek(sizeof(VdexFile::Header), kSeekSet);
+ if (actual_offset != sizeof(VdexFile::Header)) {
+ PLOG(ERROR) << "Failed to seek to the checksum location of vdex file. Actual: " << actual_offset
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) {
+ OatDexFile* oat_dex_file = &oat_dex_files_[i];
+ if (!vdex_out->WriteFully(
+ &oat_dex_file->dex_file_location_checksum_, sizeof(VdexFile::VdexChecksum))) {
+ PLOG(ERROR) << "Failed to write dex file location checksum. File: "
+ << vdex_out->GetLocation();
+ return false;
+ }
+ size_vdex_checksums_ += sizeof(VdexFile::VdexChecksum);
+ }
+
+ // Write header.
+ 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;
+ }
+
+ DCHECK_NE(vdex_dex_files_offset_, 0u);
+ DCHECK_NE(vdex_verifier_deps_offset_, 0u);
+
+ size_t dex_section_size = vdex_verifier_deps_offset_ - vdex_dex_files_offset_;
+ size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_;
+ size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_;
+
+ VdexFile::Header vdex_header(oat_dex_files_.size(),
+ dex_section_size,
+ verifier_deps_section_size,
+ quickening_info_section_size);
+ if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) {
+ PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation();
+ return false;
+ }
+ size_vdex_header_ = sizeof(VdexFile::Header);
+
+ if (!vdex_out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing to vdex file."
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ return true;
+}
+
+bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) {
+ return WriteUpTo16BytesAlignment(out, aligned_code_delta, &size_code_alignment_);
+}
+
+bool OatWriter::WriteUpTo16BytesAlignment(OutputStream* out, uint32_t size, uint32_t* stat) {
+ static const uint8_t kPadding[] = {
+ 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
+ };
+ DCHECK_LE(size, sizeof(kPadding));
+ if (UNLIKELY(!out->WriteFully(kPadding, size))) {
+ return false;
+ }
+ *stat += size;
+ return true;
+}
+
+void OatWriter::SetMultiOatRelativePatcherAdjustment() {
+ DCHECK(dex_files_ != nullptr);
+ DCHECK(relative_patcher_ != nullptr);
+ DCHECK_NE(oat_data_offset_, 0u);
+ if (image_writer_ != nullptr && !dex_files_->empty()) {
+ // The oat data begin may not be initialized yet but the oat file offset is ready.
+ size_t oat_index = image_writer_->GetOatIndexForDexFile(dex_files_->front());
+ size_t elf_file_offset = image_writer_->GetOatFileOffset(oat_index);
+ relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_);
+ }
+}
+
+OatWriter::OatDexFile::OatDexFile(const char* dex_file_location,
+ DexFileSource source,
+ CreateTypeLookupTable create_type_lookup_table)
+ : source_(source),
+ create_type_lookup_table_(create_type_lookup_table),
+ dex_file_size_(0),
+ offset_(0),
+ dex_file_location_size_(strlen(dex_file_location)),
+ dex_file_location_data_(dex_file_location),
+ dex_file_location_checksum_(0u),
+ dex_file_offset_(0u),
+ class_offsets_offset_(0u),
+ lookup_table_offset_(0u),
+ method_bss_mapping_offset_(0u),
+ dex_sections_layout_offset_(0u),
+ class_offsets_() {
+}
+
+size_t OatWriter::OatDexFile::SizeOf() const {
+ return sizeof(dex_file_location_size_)
+ + dex_file_location_size_
+ + sizeof(dex_file_location_checksum_)
+ + sizeof(dex_file_offset_)
+ + sizeof(class_offsets_offset_)
+ + sizeof(lookup_table_offset_)
+ + sizeof(method_bss_mapping_offset_)
+ + sizeof(dex_sections_layout_offset_);
+}
+
+bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) const {
+ const size_t file_offset = oat_writer->oat_data_offset_;
+ DCHECK_OFFSET_();
+
+ if (!out->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
+ PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_);
+
+ if (!out->WriteFully(dex_file_location_data_, dex_file_location_size_)) {
+ PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_;
+
+ if (!out->WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) {
+ PLOG(ERROR) << "Failed to write dex file location checksum to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_);
+
+ if (!out->WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) {
+ PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
+
+ if (!out->WriteFully(&class_offsets_offset_, sizeof(class_offsets_offset_))) {
+ PLOG(ERROR) << "Failed to write class offsets offset to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_class_offsets_offset_ += sizeof(class_offsets_offset_);
+
+ if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) {
+ PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_);
+
+ if (!out->WriteFully(&dex_sections_layout_offset_, sizeof(dex_sections_layout_offset_))) {
+ PLOG(ERROR) << "Failed to write dex section layout info to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_dex_layout_sections_offset_ += sizeof(dex_sections_layout_offset_);
+
+ if (!out->WriteFully(&method_bss_mapping_offset_, sizeof(method_bss_mapping_offset_))) {
+ PLOG(ERROR) << "Failed to write method bss mapping offset to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_dex_file_method_bss_mapping_offset_ += sizeof(method_bss_mapping_offset_);
+
+ return true;
+}
+
+bool OatWriter::OatDexFile::WriteClassOffsets(OatWriter* oat_writer, OutputStream* out) {
+ if (!out->WriteFully(class_offsets_.data(), GetClassOffsetsRawSize())) {
+ PLOG(ERROR) << "Failed to write oat class offsets for " << GetLocation()
+ << " to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_offsets_ += GetClassOffsetsRawSize();
+ return true;
+}
+
+OatWriter::OatClass::OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
+ uint32_t compiled_methods_with_code,
+ uint16_t oat_class_type)
+ : compiled_methods_(compiled_methods) {
+ const uint32_t num_methods = compiled_methods.size();
+ CHECK_LE(compiled_methods_with_code, num_methods);
+
+ oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
+
+ method_offsets_.resize(compiled_methods_with_code);
+ method_headers_.resize(compiled_methods_with_code);
+
+ uint32_t oat_method_offsets_offset_from_oat_class = OatClassHeader::SizeOf();
+ // We only create this instance if there are at least some compiled.
+ if (oat_class_type == kOatClassSomeCompiled) {
+ method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
+ method_bitmap_size_ = method_bitmap_->GetSizeOf();
+ oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
+ oat_method_offsets_offset_from_oat_class += method_bitmap_size_;
+ } else {
+ method_bitmap_ = nullptr;
+ method_bitmap_size_ = 0;
+ }
+
+ for (size_t i = 0; i < num_methods; i++) {
+ CompiledMethod* compiled_method = compiled_methods_[i];
+ if (HasCompiledCode(compiled_method)) {
+ oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class;
+ oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets);
+ if (oat_class_type == kOatClassSomeCompiled) {
+ method_bitmap_->SetBit(i);
+ }
+ } else {
+ oat_method_offsets_offsets_from_oat_class_[i] = 0;
+ }
+ }
+}
+
+size_t OatWriter::OatClass::SizeOf() const {
+ return ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+ + method_bitmap_size_
+ + (sizeof(method_offsets_[0]) * method_offsets_.size());
+}
+
+bool OatWriter::OatClassHeader::Write(OatWriter* oat_writer,
+ OutputStream* out,
+ const size_t file_offset) const {
+ DCHECK_OFFSET_();
+ if (!out->WriteFully(&status_, sizeof(status_))) {
+ PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_status_ += sizeof(status_);
+
+ if (!out->WriteFully(&type_, sizeof(type_))) {
+ PLOG(ERROR) << "Failed to write oat class type to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_type_ += sizeof(type_);
+ return true;
+}
+
+bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const {
+ if (method_bitmap_size_ != 0) {
+ if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
+ PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
+
+ if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+ PLOG(ERROR) << "Failed to write method bitmap to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
+ }
+
+ if (!out->WriteFully(method_offsets_.data(), GetMethodOffsetsRawSize())) {
+ PLOG(ERROR) << "Failed to write method offsets to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_offsets_ += GetMethodOffsetsRawSize();
+ return true;
+}
+
+const uint8_t* OatWriter::LookupBootImageInternTableSlot(const DexFile& dex_file,
+ dex::StringIndex string_idx)
+ NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking.
+ uint32_t utf16_length;
+ const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
+ DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data));
+ InternTable::Utf8String string(utf16_length,
+ utf8_data,
+ ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length));
+ const InternTable* intern_table = Runtime::Current()->GetClassLinker()->intern_table_;
+ for (const InternTable::Table::UnorderedSet& table : intern_table->strong_interns_.tables_) {
+ auto it = table.Find(string);
+ if (it != table.end()) {
+ return reinterpret_cast<const uint8_t*>(std::addressof(*it));
+ }
+ }
+ LOG(FATAL) << "Did not find boot image string " << utf8_data;
+ UNREACHABLE();
+}
+
+const uint8_t* OatWriter::LookupBootImageClassTableSlot(const DexFile& dex_file,
+ dex::TypeIndex type_idx)
+ NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking.
+ const char* descriptor = dex_file.StringByTypeIdx(type_idx);
+ ClassTable::DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor));
+ ClassTable* table = Runtime::Current()->GetClassLinker()->boot_class_table_.get();
+ for (const ClassTable::ClassSet& class_set : table->classes_) {
+ auto it = class_set.Find(pair);
+ if (it != class_set.end()) {
+ return reinterpret_cast<const uint8_t*>(std::addressof(*it));
+ }
+ }
+ LOG(FATAL) << "Did not find boot image class " << descriptor;
+ UNREACHABLE();
+}
+
+} // namespace linker
+} // namespace art
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
new file mode 100644
index 0000000..a93dd23
--- /dev/null
+++ b/dex2oat/linker/oat_writer.h
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2011 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_DEX2OAT_LINKER_OAT_WRITER_H_
+#define ART_DEX2OAT_LINKER_OAT_WRITER_H_
+
+#include <stdint.h>
+#include <cstddef>
+#include <memory>
+
+#include "base/array_ref.h"
+#include "base/dchecked_vector.h"
+#include "linker/relative_patcher.h" // For RelativePatcherTargetProvider.
+#include "mem_map.h"
+#include "method_reference.h"
+#include "mirror/class.h"
+#include "oat.h"
+#include "os.h"
+#include "safe_map.h"
+#include "string_reference.h"
+#include "type_reference.h"
+
+namespace art {
+
+class BitVector;
+class CompiledMethod;
+class CompilerDriver;
+class ProfileCompilationInfo;
+class TimingLogger;
+class TypeLookupTable;
+class VdexFile;
+class ZipEntry;
+
+namespace debug {
+struct MethodDebugInfo;
+} // namespace debug
+
+namespace verifier {
+class VerifierDeps;
+} // namespace verifier
+
+namespace linker {
+
+class ImageWriter;
+class MultiOatRelativePatcher;
+class OutputStream;
+
+// OatHeader variable length with count of D OatDexFiles
+//
+// TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile.
+// TypeLookupTable[1]
+// ...
+// TypeLookupTable[D]
+//
+// ClassOffsets[0] one table of OatClass offsets for each class def for each OatDexFile.
+// ClassOffsets[1]
+// ...
+// ClassOffsets[D]
+//
+// OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs
+// OatClass[1] contains OatClass entries with class status, offsets to code, etc.
+// ...
+// OatClass[C]
+//
+// MethodBssMapping one variable sized MethodBssMapping for each dex file, optional.
+// MethodBssMapping
+// ...
+// MethodBssMapping
+//
+// VmapTable one variable sized VmapTable blob (CodeInfo or QuickeningInfo).
+// VmapTable VmapTables are deduplicated.
+// ...
+// VmapTable
+//
+// MethodInfo one variable sized blob with MethodInfo.
+// MethodInfo MethodInfos are deduplicated.
+// ...
+// MethodInfo
+//
+// OatDexFile[0] one variable sized OatDexFile with offsets to Dex and OatClasses
+// OatDexFile[1]
+// ...
+// OatDexFile[D]
+//
+// padding if necessary so that the following code will be page aligned
+//
+// OatMethodHeader fixed size header for a CompiledMethod including the size of the MethodCode.
+// MethodCode one variable sized blob with the code of a CompiledMethod.
+// OatMethodHeader (OatMethodHeader, MethodCode) pairs are deduplicated.
+// MethodCode
+// ...
+// OatMethodHeader
+// MethodCode
+//
+class OatWriter {
+ public:
+ enum class CreateTypeLookupTable {
+ kCreate,
+ kDontCreate,
+ kDefault = kCreate
+ };
+
+ OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info);
+
+ // To produce a valid oat file, the user must first add sources with any combination of
+ // - AddDexFileSource(),
+ // - AddZippedDexFilesSource(),
+ // - AddRawDexFileSource(),
+ // - AddVdexDexFilesSource().
+ // Then the user must call in order
+ // - WriteAndOpenDexFiles()
+ // - Initialize()
+ // - WriteVerifierDeps()
+ // - WriteQuickeningInfo()
+ // - WriteChecksumsAndVdexHeader()
+ // - PrepareLayout(),
+ // - WriteRodata(),
+ // - WriteCode(),
+ // - WriteHeader().
+
+ // Add dex file source(s) from a file, either a plain dex file or
+ // a zip file with one or more dex files.
+ bool AddDexFileSource(
+ const char* filename,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+ // Add dex file source(s) from a zip file specified by a file handle.
+ bool AddZippedDexFilesSource(
+ File&& zip_fd,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+ // Add dex file source from raw memory.
+ bool AddRawDexFileSource(
+ const ArrayRef<const uint8_t>& data,
+ const char* location,
+ uint32_t location_checksum,
+ CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+ // Add dex file source(s) from a vdex file.
+ bool AddVdexDexFilesSource(
+ const VdexFile& vdex_file,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+ dchecked_vector<std::string> GetSourceLocations() const;
+
+ // 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.
+ // If `update_input_vdex` is true, then this method won't actually write the dex files,
+ // and the compiler will just re-use the existing vdex file.
+ bool WriteAndOpenDexFiles(File* vdex_file,
+ OutputStream* oat_rodata,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures* instruction_set_features,
+ SafeMap<std::string, std::string>* key_value_store,
+ bool verify,
+ bool update_input_vdex,
+ /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+ /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
+ bool WriteQuickeningInfo(OutputStream* vdex_out);
+ bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps);
+ bool WriteChecksumsAndVdexHeader(OutputStream* vdex_out);
+ // Initialize the writer with the given parameters.
+ void Initialize(const CompilerDriver* compiler,
+ ImageWriter* image_writer,
+ const std::vector<const DexFile*>& dex_files) {
+ compiler_driver_ = compiler;
+ image_writer_ = image_writer;
+ dex_files_ = &dex_files;
+ }
+
+ // Prepare layout of remaining data.
+ void PrepareLayout(MultiOatRelativePatcher* relative_patcher);
+ // Write the rest of .rodata section (ClassOffsets[], OatClass[], maps).
+ bool WriteRodata(OutputStream* out);
+ // Write the code to the .text section.
+ bool WriteCode(OutputStream* out);
+ // Write the oat header. This finalizes the oat file.
+ bool WriteHeader(OutputStream* out,
+ uint32_t image_file_location_oat_checksum,
+ uintptr_t image_file_location_oat_begin,
+ int32_t image_patch_delta);
+
+ // Returns whether the oat file has an associated image.
+ bool HasImage() const {
+ // Since the image is being created at the same time as the oat file,
+ // check if there's an image writer.
+ return image_writer_ != nullptr;
+ }
+
+ bool HasBootImage() const {
+ return compiling_boot_image_;
+ }
+
+ const OatHeader& GetOatHeader() const {
+ return *oat_header_;
+ }
+
+ size_t GetOatSize() const {
+ return oat_size_;
+ }
+
+ size_t GetBssSize() const {
+ return bss_size_;
+ }
+
+ size_t GetBssMethodsOffset() const {
+ return bss_methods_offset_;
+ }
+
+ size_t GetBssRootsOffset() const {
+ return bss_roots_offset_;
+ }
+
+ size_t GetOatDataOffset() const {
+ return oat_data_offset_;
+ }
+
+ ~OatWriter();
+
+ ArrayRef<const debug::MethodDebugInfo> GetMethodDebugInfo() const {
+ return ArrayRef<const debug::MethodDebugInfo>(method_info_);
+ }
+
+ const CompilerDriver* GetCompilerDriver() const {
+ return compiler_driver_;
+ }
+
+ private:
+ class DexFileSource;
+ class OatClassHeader;
+ class OatClass;
+ class OatDexFile;
+
+ // The function VisitDexMethods() below iterates through all the methods in all
+ // the compiled dex files in order of their definitions. The method visitor
+ // classes provide individual bits of processing for each of the passes we need to
+ // first collect the data we want to write to the oat file and then, in later passes,
+ // to actually write it.
+ class DexMethodVisitor;
+ class OatDexMethodVisitor;
+ class InitBssLayoutMethodVisitor;
+ class InitOatClassesMethodVisitor;
+ class InitCodeMethodVisitor;
+ class InitMapMethodVisitor;
+ class InitMethodInfoVisitor;
+ class InitImageMethodVisitor;
+ class WriteCodeMethodVisitor;
+ class WriteMapMethodVisitor;
+ class WriteMethodInfoVisitor;
+ class WriteQuickeningInfoMethodVisitor;
+ class WriteQuickeningIndicesMethodVisitor;
+
+ // Visit all the methods in all the compiled dex files in their definition order
+ // with a given DexMethodVisitor.
+ bool VisitDexMethods(DexMethodVisitor* visitor);
+
+ // If `update_input_vdex` is true, then this method won't actually write the dex files,
+ // and the compiler will just re-use the existing vdex file.
+ bool WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex);
+ bool WriteDexFile(OutputStream* out,
+ File* file,
+ OatDexFile* oat_dex_file,
+ bool update_input_vdex);
+ bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
+ bool LayoutAndWriteDexFile(OutputStream* out, 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 update_input_vdex);
+ 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,
+ SafeMap<std::string, std::string>* key_value_store);
+ size_t InitClassOffsets(size_t offset);
+ size_t InitOatClasses(size_t offset);
+ size_t InitOatMaps(size_t offset);
+ size_t InitMethodBssMappings(size_t offset);
+ size_t InitOatDexFiles(size_t offset);
+ size_t InitOatCode(size_t offset);
+ size_t InitOatCodeDexFiles(size_t offset);
+ void InitBssLayout(InstructionSet instruction_set);
+
+ size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset);
+ size_t WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset);
+ size_t WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset);
+ size_t WriteMethodBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset);
+ size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset);
+ size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset);
+ size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset);
+
+ bool RecordOatDataOffset(OutputStream* out);
+ bool ReadDexFileHeader(File* oat_file, OatDexFile* oat_dex_file);
+ bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location);
+ bool WriteTypeLookupTables(OutputStream* oat_rodata,
+ const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
+ bool WriteDexLayoutSections(OutputStream* oat_rodata,
+ const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
+ bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
+ bool WriteUpTo16BytesAlignment(OutputStream* out, uint32_t size, uint32_t* stat);
+ void SetMultiOatRelativePatcherAdjustment();
+ void CloseSources();
+
+ bool MayHaveCompiledMethods() const;
+
+ // Find the address of the GcRoot<String> in the InternTable for a boot image string.
+ const uint8_t* LookupBootImageInternTableSlot(const DexFile& dex_file,
+ dex::StringIndex string_idx);
+ // Find the address of the ClassTable::TableSlot for a boot image class.
+ const uint8_t* LookupBootImageClassTableSlot(const DexFile& dex_file, dex::TypeIndex type_idx);
+
+ enum class WriteState {
+ kAddingDexFileSources,
+ kPrepareLayout,
+ kWriteRoData,
+ kWriteText,
+ kWriteHeader,
+ kDone
+ };
+
+ WriteState write_state_;
+ TimingLogger* timings_;
+
+ std::vector<std::unique_ptr<File>> raw_dex_files_;
+ std::vector<std::unique_ptr<ZipArchive>> zip_archives_;
+ std::vector<std::unique_ptr<ZipEntry>> zipped_dex_files_;
+
+ // Using std::list<> which doesn't move elements around on push/emplace_back().
+ // We need this because we keep plain pointers to the strings' c_str().
+ std::list<std::string> zipped_dex_file_locations_;
+
+ dchecked_vector<debug::MethodDebugInfo> method_info_;
+
+ const CompilerDriver* compiler_driver_;
+ ImageWriter* image_writer_;
+ const bool compiling_boot_image_;
+
+ // 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_;
+
+ // Offset of section holding VerifierDeps inside Vdex.
+ size_t vdex_verifier_deps_offset_;
+
+ // Offset of section holding quickening info inside Vdex.
+ size_t vdex_quickening_info_offset_;
+
+ // Size required for Oat data structures.
+ size_t oat_size_;
+
+ // The start of the required .bss section.
+ size_t bss_start_;
+
+ // The size of the required .bss section holding the DexCache data and GC roots.
+ size_t bss_size_;
+
+ // The offset of the methods in .bss section.
+ size_t bss_methods_offset_;
+
+ // The offset of the GC roots in .bss section.
+ size_t bss_roots_offset_;
+
+ // Map for recording references to ArtMethod entries in .bss.
+ SafeMap<const DexFile*, BitVector> bss_method_entry_references_;
+
+ // Map for allocating ArtMethod entries in .bss. Indexed by MethodReference for the target
+ // method in the dex file with the "method reference value comparator" for deduplication.
+ // The value is the target offset for patching, starting at `bss_start_ + bss_methods_offset_`.
+ SafeMap<MethodReference, size_t, MethodReferenceValueComparator> bss_method_entries_;
+
+ // Map for allocating Class entries in .bss. Indexed by TypeReference for the source
+ // type in the dex file with the "type value comparator" for deduplication. The value
+ // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
+ SafeMap<TypeReference, size_t, TypeReferenceValueComparator> bss_type_entries_;
+
+ // Map for allocating String entries in .bss. Indexed by StringReference for the source
+ // string in the dex file with the "string value comparator" for deduplication. The value
+ // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`.
+ SafeMap<StringReference, size_t, StringReferenceValueComparator> bss_string_entries_;
+
+ // Whether boot image tables should be mapped to the .bss. This is needed for compiled
+ // code that reads from these tables with PC-relative instructions.
+ bool map_boot_image_tables_to_bss_;
+
+ // Offset of the oat data from the start of the mmapped region of the elf file.
+ size_t oat_data_offset_;
+
+ // Fake OatDexFiles to hold type lookup tables for the compiler.
+ std::vector<std::unique_ptr<art::OatDexFile>> type_lookup_table_oat_dex_files_;
+
+ // data to write
+ std::unique_ptr<OatHeader> oat_header_;
+ dchecked_vector<OatDexFile> oat_dex_files_;
+ dchecked_vector<OatClassHeader> oat_class_headers_;
+ dchecked_vector<OatClass> oat_classes_;
+ std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
+ std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
+ std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_;
+ std::unique_ptr<const std::vector<uint8_t>> quick_resolution_trampoline_;
+ std::unique_ptr<const std::vector<uint8_t>> quick_to_interpreter_bridge_;
+
+ // output stats
+ uint32_t size_vdex_header_;
+ uint32_t size_vdex_checksums_;
+ uint32_t size_dex_file_alignment_;
+ uint32_t size_executable_offset_alignment_;
+ uint32_t size_oat_header_;
+ uint32_t size_oat_header_key_value_store_;
+ uint32_t size_dex_file_;
+ uint32_t size_verifier_deps_;
+ uint32_t size_verifier_deps_alignment_;
+ uint32_t size_quickening_info_;
+ uint32_t size_quickening_info_alignment_;
+ uint32_t size_interpreter_to_interpreter_bridge_;
+ uint32_t size_interpreter_to_compiled_code_bridge_;
+ uint32_t size_jni_dlsym_lookup_;
+ uint32_t size_quick_generic_jni_trampoline_;
+ uint32_t size_quick_imt_conflict_trampoline_;
+ uint32_t size_quick_resolution_trampoline_;
+ uint32_t size_quick_to_interpreter_bridge_;
+ uint32_t size_trampoline_alignment_;
+ uint32_t size_method_header_;
+ uint32_t size_code_;
+ uint32_t size_code_alignment_;
+ uint32_t size_relative_call_thunks_;
+ uint32_t size_misc_thunks_;
+ uint32_t size_vmap_table_;
+ uint32_t size_method_info_;
+ uint32_t size_oat_dex_file_location_size_;
+ uint32_t size_oat_dex_file_location_data_;
+ uint32_t size_oat_dex_file_location_checksum_;
+ uint32_t size_oat_dex_file_offset_;
+ uint32_t size_oat_dex_file_class_offsets_offset_;
+ uint32_t size_oat_dex_file_lookup_table_offset_;
+ uint32_t size_oat_dex_file_dex_layout_sections_offset_;
+ uint32_t size_oat_dex_file_dex_layout_sections_;
+ uint32_t size_oat_dex_file_dex_layout_sections_alignment_;
+ uint32_t size_oat_dex_file_method_bss_mapping_offset_;
+ uint32_t size_oat_lookup_table_alignment_;
+ uint32_t size_oat_lookup_table_;
+ uint32_t size_oat_class_offsets_alignment_;
+ uint32_t size_oat_class_offsets_;
+ uint32_t size_oat_class_type_;
+ uint32_t size_oat_class_status_;
+ uint32_t size_oat_class_method_bitmaps_;
+ uint32_t size_oat_class_method_offsets_;
+ uint32_t size_method_bss_mappings_;
+
+ // The helper for processing relative patches is external so that we can patch across oat files.
+ MultiOatRelativePatcher* relative_patcher_;
+
+ // The locations of absolute patches relative to the start of the executable section.
+ dchecked_vector<uintptr_t> absolute_patch_locations_;
+
+ // Profile info used to generate new layout of files.
+ ProfileCompilationInfo* profile_compilation_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(OatWriter);
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_DEX2OAT_LINKER_OAT_WRITER_H_
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
new file mode 100644
index 0000000..0600ceb
--- /dev/null
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2011 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 "android-base/stringprintf.h"
+
+#include "arch/instruction_set_features.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+#include "class_linker.h"
+#include "common_compiler_test.h"
+#include "compiled_method.h"
+#include "compiler.h"
+#include "debug/method_debug_info.h"
+#include "dex/quick_compiler_callbacks.h"
+#include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "linker/buffered_output_stream.h"
+#include "linker/elf_writer.h"
+#include "linker/elf_writer_quick.h"
+#include "linker/file_output_stream.h"
+#include "linker/multi_oat_relative_patcher.h"
+#include "linker/vector_output_stream.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "oat_file-inl.h"
+#include "oat_writer.h"
+#include "scoped_thread_state_change-inl.h"
+#include "utils/test_dex_file_builder.h"
+
+namespace art {
+namespace linker {
+
+NO_RETURN static void Usage(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string error;
+ android::base::StringAppendV(&error, fmt, ap);
+ LOG(FATAL) << error;
+ va_end(ap);
+ UNREACHABLE();
+}
+
+class OatTest : public CommonCompilerTest {
+ protected:
+ static const bool kCompile = false; // DISABLED_ due to the time to compile libcore
+
+ void CheckMethod(ArtMethod* method,
+ const OatFile::OatMethod& oat_method,
+ const DexFile& dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
+ method->GetDexMethodIndex()));
+
+ if (compiled_method == nullptr) {
+ EXPECT_TRUE(oat_method.GetQuickCode() == nullptr) << method->PrettyMethod() << " "
+ << oat_method.GetQuickCode();
+ EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U);
+ EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
+ EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
+ } else {
+ const void* quick_oat_code = oat_method.GetQuickCode();
+ EXPECT_TRUE(quick_oat_code != nullptr) << method->PrettyMethod();
+ EXPECT_EQ(oat_method.GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes());
+ EXPECT_EQ(oat_method.GetCoreSpillMask(), compiled_method->GetCoreSpillMask());
+ EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
+ uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
+ quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
+ ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
+ EXPECT_FALSE(quick_code.empty());
+ size_t code_size = quick_code.size() * sizeof(quick_code[0]);
+ EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
+ << method->PrettyMethod() << " " << code_size;
+ CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
+ }
+ }
+
+ void SetupCompiler(Compiler::Kind compiler_kind,
+ InstructionSet insn_set,
+ const std::vector<std::string>& compiler_options,
+ /*out*/std::string* error_msg) {
+ ASSERT_TRUE(error_msg != nullptr);
+ insn_features_ = InstructionSetFeatures::FromVariant(insn_set, "default", error_msg);
+ ASSERT_TRUE(insn_features_ != nullptr) << error_msg;
+ compiler_options_.reset(new CompilerOptions);
+ for (const std::string& option : compiler_options) {
+ compiler_options_->ParseCompilerOption(option, Usage);
+ }
+ verification_results_.reset(new VerificationResults(compiler_options_.get()));
+ callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp));
+ callbacks_->SetVerificationResults(verification_results_.get());
+ Runtime::Current()->SetCompilerCallbacks(callbacks_.get());
+ timer_.reset(new CumulativeLogger("Compilation times"));
+ compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
+ compiler_kind,
+ insn_set,
+ insn_features_.get(),
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 2,
+ /* dump_stats */ true,
+ /* dump_passes */ true,
+ timer_.get(),
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr));
+ }
+
+ 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) {
+ TimingLogger timings("WriteElf", false, false);
+ OatWriter oat_writer(/*compiling_boot_image*/false,
+ &timings,
+ /*profile_compilation_info*/nullptr);
+ for (const DexFile* dex_file : dex_files) {
+ ArrayRef<const uint8_t> raw_dex_file(
+ reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
+ dex_file->GetHeader().file_size_);
+ if (!oat_writer.AddRawDexFileSource(raw_dex_file,
+ dex_file->GetLocation().c_str(),
+ dex_file->GetLocationChecksum())) {
+ return false;
+ }
+ }
+ return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify);
+ }
+
+ 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,
+ ProfileCompilationInfo* profile_compilation_info) {
+ TimingLogger timings("WriteElf", false, false);
+ OatWriter oat_writer(/*compiling_boot_image*/false, &timings, profile_compilation_info);
+ for (const char* dex_filename : dex_filenames) {
+ if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) {
+ return false;
+ }
+ }
+ return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify);
+ }
+
+ bool WriteElf(File* vdex_file,
+ File* oat_file,
+ File&& zip_fd,
+ const char* location,
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
+ TimingLogger timings("WriteElf", false, false);
+ OatWriter oat_writer(/*compiling_boot_image*/false,
+ &timings,
+ /*profile_compilation_info*/nullptr);
+ if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
+ return false;
+ }
+ return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify);
+ }
+
+ bool DoWriteElf(File* vdex_file,
+ File* oat_file,
+ OatWriter& oat_writer,
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
+ std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
+ compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
+ &compiler_driver_->GetCompilerOptions(),
+ oat_file);
+ elf_writer->Start();
+ 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(kIsVdexEnabled ? vdex_file : oat_file,
+ oat_rodata,
+ compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
+ &key_value_store,
+ verify,
+ /* update_input_vdex */ false,
+ &opened_dex_files_map,
+ &opened_dex_files)) {
+ return false;
+ }
+
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* const class_linker = runtime->GetClassLinker();
+ std::vector<const DexFile*> dex_files;
+ for (const std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
+ dex_files.push_back(dex_file.get());
+ ScopedObjectAccess soa(Thread::Current());
+ class_linker->RegisterDexFile(*dex_file, nullptr);
+ }
+ MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(),
+ instruction_set_features_.get());
+ oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files);
+ oat_writer.PrepareLayout(&patcher);
+ size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
+ size_t text_size = oat_writer.GetOatSize() - rodata_size;
+ elf_writer->PrepareDynamicSection(rodata_size,
+ text_size,
+ oat_writer.GetBssSize(),
+ oat_writer.GetBssMethodsOffset(),
+ oat_writer.GetBssRootsOffset());
+
+ if (kIsVdexEnabled) {
+ std::unique_ptr<BufferedOutputStream> vdex_out =
+ std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
+ if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) {
+ return false;
+ }
+ if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) {
+ return false;
+ }
+ }
+
+ if (!oat_writer.WriteRodata(oat_rodata)) {
+ return false;
+ }
+ elf_writer->EndRoData(oat_rodata);
+
+ OutputStream* text = elf_writer->StartText();
+ if (!oat_writer.WriteCode(text)) {
+ return false;
+ }
+ elf_writer->EndText(text);
+
+ if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) {
+ return false;
+ }
+
+ elf_writer->WriteDynamicSection();
+ elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
+
+ if (!elf_writer->End()) {
+ return false;
+ }
+
+ opened_dex_files_maps_.emplace_back(std::move(opened_dex_files_map));
+ for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
+ opened_dex_files_.emplace_back(dex_file.release());
+ }
+ return true;
+ }
+
+ void TestDexFileInput(bool verify, bool low_4gb, bool use_profile);
+ void TestZipFileInput(bool verify);
+ void TestZipFileInputWithEmptyDex();
+
+ std::unique_ptr<const InstructionSetFeatures> insn_features_;
+ std::unique_ptr<QuickCompilerCallbacks> callbacks_;
+
+ std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
+};
+
+class ZipBuilder {
+ public:
+ explicit ZipBuilder(File* zip_file) : zip_file_(zip_file) { }
+
+ bool AddFile(const char* location, const void* data, size_t size) {
+ off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR);
+ if (offset == static_cast<off_t>(-1)) {
+ return false;
+ }
+
+ ZipFileHeader file_header;
+ file_header.crc32 = crc32(0u, reinterpret_cast<const Bytef*>(data), size);
+ file_header.compressed_size = size;
+ file_header.uncompressed_size = size;
+ file_header.filename_length = strlen(location);
+
+ if (!zip_file_->WriteFully(&file_header, sizeof(file_header)) ||
+ !zip_file_->WriteFully(location, file_header.filename_length) ||
+ !zip_file_->WriteFully(data, size)) {
+ return false;
+ }
+
+ CentralDirectoryFileHeader cdfh;
+ cdfh.crc32 = file_header.crc32;
+ cdfh.compressed_size = size;
+ cdfh.uncompressed_size = size;
+ cdfh.filename_length = file_header.filename_length;
+ cdfh.relative_offset_of_local_file_header = offset;
+ file_data_.push_back(FileData { cdfh, location });
+ return true;
+ }
+
+ bool Finish() {
+ off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR);
+ if (offset == static_cast<off_t>(-1)) {
+ return false;
+ }
+
+ size_t central_directory_size = 0u;
+ for (const FileData& file_data : file_data_) {
+ if (!zip_file_->WriteFully(&file_data.cdfh, sizeof(file_data.cdfh)) ||
+ !zip_file_->WriteFully(file_data.location, file_data.cdfh.filename_length)) {
+ return false;
+ }
+ central_directory_size += sizeof(file_data.cdfh) + file_data.cdfh.filename_length;
+ }
+ EndOfCentralDirectoryRecord eocd_record;
+ eocd_record.number_of_central_directory_records_on_this_disk = file_data_.size();
+ eocd_record.total_number_of_central_directory_records = file_data_.size();
+ eocd_record.size_of_central_directory = central_directory_size;
+ eocd_record.offset_of_start_of_central_directory = offset;
+ return
+ zip_file_->WriteFully(&eocd_record, sizeof(eocd_record)) &&
+ zip_file_->Flush() == 0;
+ }
+
+ private:
+ struct PACKED(1) ZipFileHeader {
+ uint32_t signature = 0x04034b50;
+ uint16_t version_needed_to_extract = 10;
+ uint16_t general_purpose_bit_flag = 0;
+ uint16_t compression_method = 0; // 0 = store only.
+ uint16_t file_last_modification_time = 0u;
+ uint16_t file_last_modification_date = 0u;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t filename_length;
+ uint16_t extra_field_length = 0u; // No extra fields.
+ };
+
+ struct PACKED(1) CentralDirectoryFileHeader {
+ uint32_t signature = 0x02014b50;
+ uint16_t version_made_by = 10;
+ uint16_t version_needed_to_extract = 10;
+ uint16_t general_purpose_bit_flag = 0;
+ uint16_t compression_method = 0; // 0 = store only.
+ uint16_t file_last_modification_time = 0u;
+ uint16_t file_last_modification_date = 0u;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t filename_length;
+ uint16_t extra_field_length = 0u; // No extra fields.
+ uint16_t file_comment_length = 0u; // No file comment.
+ uint16_t disk_number_where_file_starts = 0u;
+ uint16_t internal_file_attributes = 0u;
+ uint32_t external_file_attributes = 0u;
+ uint32_t relative_offset_of_local_file_header;
+ };
+
+ struct PACKED(1) EndOfCentralDirectoryRecord {
+ uint32_t signature = 0x06054b50;
+ uint16_t number_of_this_disk = 0u;
+ uint16_t disk_where_central_directory_starts = 0u;
+ uint16_t number_of_central_directory_records_on_this_disk;
+ uint16_t total_number_of_central_directory_records;
+ uint32_t size_of_central_directory;
+ uint32_t offset_of_start_of_central_directory;
+ uint16_t comment_length = 0u; // No file comment.
+ };
+
+ struct FileData {
+ CentralDirectoryFileHeader cdfh;
+ const char* location;
+ };
+
+ File* zip_file_;
+ std::vector<FileData> file_data_;
+};
+
+TEST_F(OatTest, WriteRead) {
+ TimingLogger timings("OatTest::WriteRead", false, false);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ // TODO: make selectable.
+ Compiler::Kind compiler_kind = Compiler::kQuick;
+ InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
+ std::string error_msg;
+ SetupCompiler(compiler_kind, insn_set, std::vector<std::string>(), /*out*/ &error_msg);
+
+ jobject class_loader = nullptr;
+ if (kCompile) {
+ TimingLogger timings2("OatTest::WriteRead", false, false);
+ compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
+ compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
+ }
+
+ 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_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_oat.GetFilename(),
+ tmp_oat.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/true,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ ASSERT_TRUE(oat_header.IsValid());
+ ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core
+ ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
+ ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
+ ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
+
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ const DexFile& dex_file = *java_lang_dex_file_;
+ uint32_t dex_file_checksum = dex_file.GetLocationChecksum();
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
+ &dex_file_checksum);
+ ASSERT_TRUE(oat_dex_file != nullptr);
+ CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
+ ScopedObjectAccess soa(Thread::Current());
+ auto pointer_size = class_linker->GetImagePointerSize();
+ for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+
+ size_t num_virtual_methods = 0;
+ if (class_data != nullptr) {
+ ClassDataItemIterator it(dex_file, class_data);
+ num_virtual_methods = it.NumVirtualMethods();
+ }
+
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ mirror::Class* klass = class_linker->FindClass(soa.Self(),
+ descriptor,
+ ScopedNullHandle<mirror::ClassLoader>());
+
+ const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i);
+ CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class.GetStatus()) << descriptor;
+ CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled,
+ oat_class.GetType()) << descriptor;
+
+ size_t method_index = 0;
+ for (auto& m : klass->GetDirectMethods(pointer_size)) {
+ CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
+ ++method_index;
+ }
+ size_t visited_virtuals = 0;
+ // TODO We should also check copied methods in this test.
+ for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) {
+ if (!klass->IsInterface()) {
+ EXPECT_FALSE(m.IsCopied());
+ }
+ CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
+ ++method_index;
+ ++visited_virtuals;
+ }
+ EXPECT_EQ(visited_virtuals, num_virtual_methods);
+ }
+}
+
+TEST_F(OatTest, OatHeaderSizeCheck) {
+ // If this test is failing and you have to update these constants,
+ // it is time to update OatHeader::kOatVersion
+ EXPECT_EQ(76U, sizeof(OatHeader));
+ EXPECT_EQ(4U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
+ EXPECT_EQ(161 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ sizeof(QuickEntryPoints));
+}
+
+TEST_F(OatTest, OatHeaderIsValid) {
+ InstructionSet insn_set = kX86;
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> insn_features(
+ InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
+ ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
+ std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set,
+ insn_features.get(),
+ 0u,
+ nullptr));
+ ASSERT_NE(oat_header.get(), nullptr);
+ ASSERT_TRUE(oat_header->IsValid());
+
+ char* magic = const_cast<char*>(oat_header->GetMagic());
+ strcpy(magic, ""); // bad magic
+ ASSERT_FALSE(oat_header->IsValid());
+ strcpy(magic, "oat\n000"); // bad version
+ ASSERT_FALSE(oat_header->IsValid());
+}
+
+TEST_F(OatTest, EmptyTextSection) {
+ TimingLogger timings("OatTest::EmptyTextSection", false, false);
+
+ // TODO: make selectable.
+ Compiler::Kind compiler_kind = Compiler::kQuick;
+ InstructionSet insn_set = kRuntimeISA;
+ if (insn_set == kArm) insn_set = kThumb2;
+ std::string error_msg;
+ std::vector<std::string> compiler_options;
+ compiler_options.push_back("--compiler-filter=extract");
+ SetupCompiler(compiler_kind, insn_set, compiler_options, /*out*/ &error_msg);
+
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ class_loader = LoadDex("Main");
+ }
+ ASSERT_TRUE(class_loader != nullptr);
+ std::vector<const DexFile*> dex_files = GetDexFiles(class_loader);
+ ASSERT_TRUE(!dex_files.empty());
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ for (const DexFile* dex_file : dex_files) {
+ ScopedObjectAccess soa(Thread::Current());
+ class_linker->RegisterDexFile(*dex_file,
+ soa.Decode<mirror::ClassLoader>(class_loader).Ptr());
+ }
+ compiler_driver_->SetDexFilesForOatFile(dex_files);
+ compiler_driver_->CompileAll(class_loader, dex_files, &timings);
+
+ 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_vdex.GetFile(), tmp_oat.GetFile(), dex_files, key_value_store, false);
+ ASSERT_TRUE(success);
+
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+ tmp_oat.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(oat_file != nullptr);
+ 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) {
+ // If in verify mode (= fail the verifier mode), make sure we fail early. We'll fail already
+ // because of the missing map, but that may lead to out of bounds reads.
+ if (verify) {
+ const_cast<DexFile::Header*>(&data->GetHeader())->checksum_++;
+ }
+}
+
+void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) {
+ TimingLogger timings("OatTest::DexFileInput", false, false);
+
+ std::vector<const char*> input_filenames;
+
+ ScratchFile dex_file1;
+ TestDexFileBuilder builder1;
+ builder1.AddField("Lsome.TestClass;", "int", "someField");
+ builder1.AddMethod("Lsome.TestClass;", "()I", "foo");
+ std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file1_data);
+
+ bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
+ dex_file1_data->GetHeader().file_size_);
+ ASSERT_TRUE(success);
+ success = dex_file1.GetFile()->Flush() == 0;
+ ASSERT_TRUE(success);
+ input_filenames.push_back(dex_file1.GetFilename().c_str());
+
+ ScratchFile dex_file2;
+ TestDexFileBuilder builder2;
+ builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
+ builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
+ std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file2_data);
+
+ success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
+ dex_file2_data->GetHeader().file_size_);
+ ASSERT_TRUE(success);
+ success = dex_file2.GetFile()->Flush() == 0;
+ ASSERT_TRUE(success);
+ input_filenames.push_back(dex_file2.GetFilename().c_str());
+
+ ScratchFile oat_file, vdex_file(oat_file, ".vdex");
+ SafeMap<std::string, std::string> key_value_store;
+ key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
+ std::unique_ptr<ProfileCompilationInfo>
+ profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
+ success = WriteElf(vdex_file.GetFile(),
+ oat_file.GetFile(),
+ input_filenames,
+ key_value_store,
+ verify,
+ profile_compilation_info.get());
+
+ // In verify mode, we expect failure.
+ if (verify) {
+ ASSERT_FALSE(success);
+ return;
+ }
+
+ ASSERT_TRUE(success);
+
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ low_4gb,
+ nullptr,
+ &error_msg));
+ if (low_4gb) {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin());
+ EXPECT_EQ(begin, static_cast<uint32_t>(begin));
+ }
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(dex_file1_data->GetLocation(), opened_dex_file1->GetLocation());
+
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation());
+}
+
+TEST_F(OatTest, DexFileInputCheckOutput) {
+ TestDexFileInput(/*verify*/false, /*low_4gb*/false, /*use_profile*/false);
+}
+
+TEST_F(OatTest, DexFileInputCheckOutputLow4GB) {
+ TestDexFileInput(/*verify*/false, /*low_4gb*/true, /*use_profile*/false);
+}
+
+TEST_F(OatTest, DexFileInputCheckVerifier) {
+ TestDexFileInput(/*verify*/true, /*low_4gb*/false, /*use_profile*/false);
+}
+
+TEST_F(OatTest, DexFileFailsVerifierWithLayout) {
+ TestDexFileInput(/*verify*/true, /*low_4gb*/false, /*use_profile*/true);
+}
+
+void OatTest::TestZipFileInput(bool verify) {
+ TimingLogger timings("OatTest::DexFileInput", false, false);
+
+ ScratchFile zip_file;
+ ZipBuilder zip_builder(zip_file.GetFile());
+
+ ScratchFile dex_file1;
+ TestDexFileBuilder builder1;
+ builder1.AddField("Lsome.TestClass;", "long", "someField");
+ builder1.AddMethod("Lsome.TestClass;", "()D", "foo");
+ std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file1_data);
+
+ bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
+ dex_file1_data->GetHeader().file_size_);
+ ASSERT_TRUE(success);
+ success = dex_file1.GetFile()->Flush() == 0;
+ ASSERT_TRUE(success);
+ success = zip_builder.AddFile("classes.dex",
+ &dex_file1_data->GetHeader(),
+ dex_file1_data->GetHeader().file_size_);
+ ASSERT_TRUE(success);
+
+ ScratchFile dex_file2;
+ TestDexFileBuilder builder2;
+ builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
+ builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
+ std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file2_data);
+
+ success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
+ dex_file2_data->GetHeader().file_size_);
+ ASSERT_TRUE(success);
+ success = dex_file2.GetFile()->Flush() == 0;
+ ASSERT_TRUE(success);
+ success = zip_builder.AddFile("classes2.dex",
+ &dex_file2_data->GetHeader(),
+ dex_file2_data->GetHeader().file_size_);
+ ASSERT_TRUE(success);
+
+ success = zip_builder.Finish();
+ ASSERT_TRUE(success) << strerror(errno);
+
+ SafeMap<std::string, std::string> key_value_store;
+ key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
+ {
+ // 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, vdex_file(oat_file, ".vdex");
+ success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames,
+ key_value_store, verify, /*profile_compilation_info*/nullptr);
+
+ if (verify) {
+ ASSERT_FALSE(success);
+ } else {
+ ASSERT_TRUE(success);
+
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ opened_dex_file1->GetLocation());
+
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ opened_dex_file2->GetLocation());
+ }
+ }
+
+ {
+ // Test using the AddZipDexFileSource() interface with the zip file handle.
+ File zip_fd(dup(zip_file.GetFd()), /* check_usage */ false);
+ ASSERT_NE(-1, zip_fd.Fd());
+
+ 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,
+ verify);
+ if (verify) {
+ ASSERT_FALSE(success);
+ } else {
+ ASSERT_TRUE(success);
+
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ opened_dex_file1->GetLocation());
+
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ opened_dex_file2->GetLocation());
+ }
+ }
+}
+
+TEST_F(OatTest, ZipFileInputCheckOutput) {
+ TestZipFileInput(false);
+}
+
+TEST_F(OatTest, ZipFileInputCheckVerifier) {
+ TestZipFileInput(true);
+}
+
+void OatTest::TestZipFileInputWithEmptyDex() {
+ ScratchFile zip_file;
+ ZipBuilder zip_builder(zip_file.GetFile());
+ bool success = zip_builder.AddFile("classes.dex", nullptr, 0);
+ ASSERT_TRUE(success);
+ success = zip_builder.Finish();
+ ASSERT_TRUE(success) << strerror(errno);
+
+ SafeMap<std::string, std::string> key_value_store;
+ key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
+ std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4]
+ ScratchFile oat_file, vdex_file(oat_file, ".vdex");
+ std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo());
+ success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames,
+ key_value_store, /*verify*/false, profile_compilation_info.get());
+ ASSERT_FALSE(success);
+}
+
+TEST_F(OatTest, ZipFileInputWithEmptyDex) {
+ TestZipFileInputWithEmptyDex();
+}
+
+TEST_F(OatTest, UpdateChecksum) {
+ InstructionSet insn_set = kX86;
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> insn_features(
+ InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
+ ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
+ std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set,
+ insn_features.get(),
+ 0u,
+ nullptr));
+ // The starting adler32 value is 1.
+ EXPECT_EQ(1U, oat_header->GetChecksum());
+
+ oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic));
+ EXPECT_EQ(64291151U, oat_header->GetChecksum());
+
+ // Make sure that null data does not reset the checksum.
+ oat_header->UpdateChecksum(nullptr, 0);
+ EXPECT_EQ(64291151U, oat_header->GetChecksum());
+
+ oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic));
+ EXPECT_EQ(216138397U, oat_header->GetChecksum());
+}
+
+} // namespace linker
+} // namespace art