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/compiler/Android.bp b/compiler/Android.bp
index c798d97..1475679 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -38,7 +38,6 @@
"driver/dex_compilation_unit.cc",
"linker/buffered_output_stream.cc",
"linker/file_output_stream.cc",
- "linker/multi_oat_relative_patcher.cc",
"linker/output_stream.cc",
"linker/vector_output_stream.cc",
"linker/relative_patcher.cc",
@@ -95,10 +94,6 @@
"utils/jni_macro_assembler.cc",
"utils/swap_space.cc",
"compiler.cc",
- "elf_writer.cc",
- "elf_writer_quick.cc",
- "image_writer.cc",
- "oat_writer.cc",
],
codegen: {
@@ -198,19 +193,10 @@
generated_sources: ["art_compiler_operator_srcs"],
shared_libs: [
"libbase",
- "liblz4",
"liblzma",
],
include_dirs: ["art/disassembler"],
export_include_dirs: ["."],
-
- // For SHA-1 checksumming of build ID
- static: {
- whole_static_libs: ["libcrypto"],
- },
- shared: {
- shared_libs: ["libcrypto"],
- },
}
gensrcs {
@@ -222,7 +208,6 @@
"dex/dex_to_dex_compiler.h",
"driver/compiler_driver.h",
"driver/compiler_options.h",
- "image_writer.h",
"optimizing/locations.h",
"utils/arm/constants_arm.h",
@@ -265,7 +250,6 @@
},
shared_libs: [
"libart",
- "libart-dexlayout",
],
}
@@ -305,7 +289,6 @@
},
shared_libs: [
"libartd",
- "libartd-dexlayout"
],
}
@@ -332,15 +315,10 @@
"dex/dex_to_dex_decompiler_test.cc",
"driver/compiled_method_storage_test.cc",
"driver/compiler_driver_test.cc",
- "elf_writer_test.cc",
"exception_test.cc",
- "image_test.cc",
- "image_write_read_test.cc",
"jni/jni_compiler_test.cc",
"linker/method_bss_mapping_encoder_test.cc",
- "linker/multi_oat_relative_patcher_test.cc",
"linker/output_stream_test.cc",
- "oat_test.cc",
"optimizing/bounds_check_elimination_test.cc",
"optimizing/dominator_test.cc",
"optimizing/find_loops_test.cc",
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index e1f538d..b30ff14 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -28,8 +28,8 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
-#include "elf_builder.h"
#include "gtest/gtest.h"
+#include "linker/elf_builder.h"
#include "linker/file_output_stream.h"
#include "os.h"
@@ -62,8 +62,8 @@
// Write simple elf file with just the DWARF sections.
InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
ScratchFile file;
- FileOutputStream output_stream(file.GetFile());
- ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream);
+ linker::FileOutputStream output_stream(file.GetFile());
+ linker::ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream);
builder.Start();
if (!debug_info_data_.empty()) {
builder.WriteSection(".debug_info", &debug_info_data_);
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index f9d33c1..6dacdfa 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -24,7 +24,7 @@
#include "debug/dwarf/dwarf_constants.h"
#include "debug/dwarf/headers.h"
#include "debug/method_debug_info.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
namespace art {
namespace debug {
@@ -168,7 +168,7 @@
}
template<typename ElfTypes>
-void WriteCFISection(ElfBuilder<ElfTypes>* builder,
+void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat format,
bool write_oat_patches) {
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 2801240..2b61727 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -29,9 +29,9 @@
#include "debug/method_debug_info.h"
#include "dex_file-inl.h"
#include "dex_file.h"
-#include "elf_builder.h"
#include "heap_poisoning.h"
#include "linear_alloc.h"
+#include "linker/elf_builder.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
@@ -68,7 +68,7 @@
using Elf_Addr = typename ElfTypes::Addr;
public:
- explicit ElfDebugInfoWriter(ElfBuilder<ElfTypes>* builder)
+ explicit ElfDebugInfoWriter(linker::ElfBuilder<ElfTypes>* builder)
: builder_(builder),
debug_abbrev_(&debug_abbrev_buffer_) {
}
@@ -93,7 +93,7 @@
}
private:
- ElfBuilder<ElfTypes>* builder_;
+ linker::ElfBuilder<ElfTypes>* builder_;
std::vector<uintptr_t> debug_info_patches_;
std::vector<uint8_t> debug_abbrev_buffer_;
dwarf::DebugAbbrevWriter<> debug_abbrev_;
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index cdd1e53..cf5d65e 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -25,7 +25,7 @@
#include "debug/dwarf/headers.h"
#include "debug/elf_compilation_unit.h"
#include "dex_file-inl.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
#include "stack_map.h"
namespace art {
@@ -43,7 +43,7 @@
using Elf_Addr = typename ElfTypes::Addr;
public:
- explicit ElfDebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
+ explicit ElfDebugLineWriter(linker::ElfBuilder<ElfTypes>* builder) : builder_(builder) {
}
void Start() {
@@ -280,7 +280,7 @@
}
private:
- ElfBuilder<ElfTypes>* builder_;
+ linker::ElfBuilder<ElfTypes>* builder_;
std::vector<uintptr_t> debug_line_patches_;
};
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index c5ff858..33c46d7 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -29,7 +29,7 @@
#include "debug/elf_gnu_debugdata_writer.h"
#include "debug/elf_symtab_writer.h"
#include "debug/method_debug_info.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
#include "linker/vector_output_stream.h"
#include "oat.h"
@@ -37,7 +37,7 @@
namespace debug {
template <typename ElfTypes>
-void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
+void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat cfi_format,
bool write_oat_patches) {
@@ -133,8 +133,9 @@
const ArrayRef<const MethodDebugInfo>& method_infos) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
- VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
+ linker::VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder(
+ new linker::ElfBuilder<ElfTypes>(isa, features, &out));
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
WriteDebugInfo(builder.get(),
@@ -165,8 +166,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
- VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
+ linker::VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder(
+ new linker::ElfBuilder<ElfTypes>(isa, features, &out));
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
@@ -192,12 +194,12 @@
// Explicit instantiations
template void WriteDebugInfo<ElfTypes32>(
- ElfBuilder<ElfTypes32>* builder,
+ linker::ElfBuilder<ElfTypes32>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat cfi_format,
bool write_oat_patches);
template void WriteDebugInfo<ElfTypes64>(
- ElfBuilder<ElfTypes64>* builder,
+ linker::ElfBuilder<ElfTypes64>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat cfi_format,
bool write_oat_patches);
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 6e26ba3..d24ca9b 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -23,7 +23,7 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "debug/dwarf/dwarf_constants.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
namespace art {
class OatHeader;
@@ -35,7 +35,7 @@
template <typename ElfTypes>
void WriteDebugInfo(
- ElfBuilder<ElfTypes>* builder,
+ linker::ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat cfi_format,
bool write_oat_patches);
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
index fb63d62..1cdf6b0 100644
--- a/compiler/debug/elf_gnu_debugdata_writer.h
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -20,7 +20,7 @@
#include <vector>
#include "arch/instruction_set.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
#include "linker/vector_output_stream.h"
// liblzma.
@@ -85,8 +85,9 @@
const ArrayRef<const MethodDebugInfo>& method_infos) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
- VectorOutputStream out("Mini-debug-info ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
+ linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer);
+ std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder(
+ new linker::ElfBuilder<ElfTypes>(isa, features, &out));
builder->Start();
// Mirror .rodata and .text as NOBITS sections.
// It is needed to detected relocations after compression.
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index abd2699..b37f984 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -20,7 +20,7 @@
#include <unordered_set>
#include "debug/method_debug_info.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
#include "utils.h"
namespace art {
@@ -36,7 +36,7 @@
constexpr bool kGenerateSingleArmMappingSymbol = true;
template <typename ElfTypes>
-static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
+static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
bool with_signature) {
uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max();
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
deleted file mode 100644
index 37e4f11..0000000
--- a/compiler/elf_writer.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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 {
-
-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 art
diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h
deleted file mode 100644
index a8a5bc3..0000000
--- a/compiler/elf_writer.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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_COMPILER_ELF_WRITER_H_
-#define ART_COMPILER_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;
-class OutputStream;
-
-namespace debug {
-struct MethodDebugInfo;
-} // namespace debug
-
-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 art
-
-#endif // ART_COMPILER_ELF_WRITER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
deleted file mode 100644
index 5d6dd2e..0000000
--- a/compiler/elf_writer_quick.cc
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * 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_builder.h"
-#include "elf_utils.h"
-#include "globals.h"
-#include "leb128.h"
-#include "linker/buffered_output_stream.h"
-#include "linker/file_output_stream.h"
-#include "thread-current-inl.h"
-#include "thread_pool.h"
-#include "utils.h"
-
-namespace art {
-
-// .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 art
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
deleted file mode 100644
index 3d5dd39..0000000
--- a/compiler/elf_writer_quick.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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_COMPILER_ELF_WRITER_QUICK_H_
-#define ART_COMPILER_ELF_WRITER_QUICK_H_
-
-#include <memory>
-
-#include "arch/instruction_set.h"
-#include "elf_writer.h"
-#include "os.h"
-
-namespace art {
-
-class CompilerOptions;
-class InstructionSetFeatures;
-
-std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- const CompilerOptions* compiler_options,
- File* elf_file);
-
-} // namespace art
-
-#endif // ART_COMPILER_ELF_WRITER_QUICK_H_
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
deleted file mode 100644
index 984e9ee..0000000
--- a/compiler/elf_writer_test.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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_builder.h"
-#include "elf_file.h"
-#include "elf_file_impl.h"
-#include "elf_writer_quick.h"
-#include "oat.h"
-#include "utils.h"
-
-namespace art {
-
-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 art
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
deleted file mode 100644
index 7b623dd..0000000
--- a/compiler/image_test.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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 {
-
-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 art
diff --git a/compiler/image_test.h b/compiler/image_test.h
deleted file mode 100644
index f1adedd..0000000
--- a/compiler/image_test.h
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * 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_COMPILER_IMAGE_TEST_H_
-#define ART_COMPILER_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 "elf_writer.h"
-#include "elf_writer_quick.h"
-#include "gc/space/image_space.h"
-#include "image_writer.h"
-#include "linker/buffered_output_stream.h"
-#include "linker/file_output_stream.h"
-#include "linker/multi_oat_relative_patcher.h"
-#include "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 {
-
-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 art
-
-#endif // ART_COMPILER_IMAGE_TEST_H_
diff --git a/compiler/image_write_read_test.cc b/compiler/image_write_read_test.cc
deleted file mode 100644
index 32c0b06..0000000
--- a/compiler/image_write_read_test.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 {
-
-TEST_F(ImageTest, WriteReadUncompressed) {
- TestWriteRead(ImageHeader::kStorageModeUncompressed);
-}
-
-TEST_F(ImageTest, WriteReadLZ4) {
- TestWriteRead(ImageHeader::kStorageModeLZ4);
-}
-
-TEST_F(ImageTest, WriteReadLZ4HC) {
- TestWriteRead(ImageHeader::kStorageModeLZ4HC);
-}
-
-} // namespace art
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
deleted file mode 100644
index 4ffe238..0000000
--- a/compiler/image_writer.cc
+++ /dev/null
@@ -1,2839 +0,0 @@
-/*
- * 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 "elf_writer.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 {
-
-// 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 art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
deleted file mode 100644
index 2fc394e..0000000
--- a/compiler/image_writer.h
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * 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_COMPILER_IMAGE_WRITER_H_
-#define ART_COMPILER_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;
-
-// 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 art
-
-#endif // ART_COMPILER_IMAGE_WRITER_H_
diff --git a/compiler/linker/buffered_output_stream.cc b/compiler/linker/buffered_output_stream.cc
index 4c66c76..07066b7 100644
--- a/compiler/linker/buffered_output_stream.cc
+++ b/compiler/linker/buffered_output_stream.cc
@@ -19,6 +19,7 @@
#include <string.h>
namespace art {
+namespace linker {
BufferedOutputStream::BufferedOutputStream(std::unique_ptr<OutputStream> out)
: OutputStream(out->GetLocation()), // Before out is moved to out_.
@@ -67,4 +68,5 @@
return out_->Seek(offset, whence);
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/buffered_output_stream.h b/compiler/linker/buffered_output_stream.h
index a2eefbb..66994e8 100644
--- a/compiler/linker/buffered_output_stream.h
+++ b/compiler/linker/buffered_output_stream.h
@@ -24,6 +24,7 @@
#include "globals.h"
namespace art {
+namespace linker {
class BufferedOutputStream FINAL : public OutputStream {
public:
@@ -49,6 +50,7 @@
DISALLOW_COPY_AND_ASSIGN(BufferedOutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_BUFFERED_OUTPUT_STREAM_H_
diff --git a/compiler/elf_builder.h b/compiler/linker/elf_builder.h
similarity index 99%
rename from compiler/elf_builder.h
rename to compiler/linker/elf_builder.h
index 2ef9fa1..7941237 100644
--- a/compiler/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_ELF_BUILDER_H_
-#define ART_COMPILER_ELF_BUILDER_H_
+#ifndef ART_COMPILER_LINKER_ELF_BUILDER_H_
+#define ART_COMPILER_LINKER_ELF_BUILDER_H_
#include <vector>
@@ -30,6 +30,7 @@
#include "linker/error_delaying_output_stream.h"
namespace art {
+namespace linker {
// Writes ELF file.
//
@@ -1021,6 +1022,7 @@
DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
};
+} // namespace linker
} // namespace art
-#endif // ART_COMPILER_ELF_BUILDER_H_
+#endif // ART_COMPILER_LINKER_ELF_BUILDER_H_
diff --git a/compiler/linker/error_delaying_output_stream.h b/compiler/linker/error_delaying_output_stream.h
index 99410e4..33e6b5a 100644
--- a/compiler/linker/error_delaying_output_stream.h
+++ b/compiler/linker/error_delaying_output_stream.h
@@ -22,6 +22,7 @@
#include "base/logging.h"
namespace art {
+namespace linker {
// OutputStream wrapper that delays reporting an error until Flush().
class ErrorDelayingOutputStream FINAL : public OutputStream {
@@ -96,6 +97,7 @@
off_t output_offset_; // Keep track of the current position in the stream.
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_ERROR_DELAYING_OUTPUT_STREAM_H_
diff --git a/compiler/linker/file_output_stream.cc b/compiler/linker/file_output_stream.cc
index bbfbdfd..477846e 100644
--- a/compiler/linker/file_output_stream.cc
+++ b/compiler/linker/file_output_stream.cc
@@ -22,6 +22,7 @@
#include "base/unix_file/fd_file.h"
namespace art {
+namespace linker {
FileOutputStream::FileOutputStream(File* file) : OutputStream(file->GetPath()), file_(file) {}
@@ -37,4 +38,5 @@
return file_->Flush() == 0;
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/file_output_stream.h b/compiler/linker/file_output_stream.h
index f2d8453..28296a4 100644
--- a/compiler/linker/file_output_stream.h
+++ b/compiler/linker/file_output_stream.h
@@ -22,6 +22,7 @@
#include "os.h"
namespace art {
+namespace linker {
class FileOutputStream FINAL : public OutputStream {
public:
@@ -41,6 +42,7 @@
DISALLOW_COPY_AND_ASSIGN(FileOutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_
diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc
deleted file mode 100644
index 4ae75d6..0000000
--- a/compiler/linker/multi_oat_relative_patcher.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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_(
- linker::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/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
deleted file mode 100644
index 02cd4b0..0000000
--- a/compiler/linker/multi_oat_relative_patcher.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
-#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
-
-#include "arch/instruction_set.h"
-#include "debug/method_debug_info.h"
-#include "method_reference.h"
-#include "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 linker::RelativePatcherTargetProvider.
- class MethodOffsetMap : public linker::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_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
deleted file mode 100644
index 5c359dc..0000000
--- a/compiler/linker/multi_oat_relative_patcher_test.cc
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * 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 "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/compiler/linker/output_stream.cc b/compiler/linker/output_stream.cc
index a8b64ca..f5a1913 100644
--- a/compiler/linker/output_stream.cc
+++ b/compiler/linker/output_stream.cc
@@ -17,6 +17,7 @@
#include "output_stream.h"
namespace art {
+namespace linker {
std::ostream& operator<<(std::ostream& os, const Whence& rhs) {
switch (rhs) {
@@ -28,4 +29,5 @@
return os;
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/output_stream.h b/compiler/linker/output_stream.h
index 96a5f48..5310e2f 100644
--- a/compiler/linker/output_stream.h
+++ b/compiler/linker/output_stream.h
@@ -23,6 +23,7 @@
#include "base/macros.h"
namespace art {
+namespace linker {
enum Whence {
kSeekSet = SEEK_SET,
@@ -59,6 +60,7 @@
DISALLOW_COPY_AND_ASSIGN(OutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_OUTPUT_STREAM_H_
diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc
index 87cb100..ad29840 100644
--- a/compiler/linker/output_stream_test.cc
+++ b/compiler/linker/output_stream_test.cc
@@ -23,6 +23,7 @@
#include "common_runtime_test.h"
namespace art {
+namespace linker {
class OutputStreamTest : public CommonRuntimeTest {
protected:
@@ -133,4 +134,5 @@
ASSERT_TRUE(checking_output_stream->flush_called);
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index 53a0966..e079946 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -29,7 +29,6 @@
class CompiledMethod;
class LinkerPatch;
-class OutputStream;
namespace debug {
struct MethodDebugInfo;
@@ -37,6 +36,8 @@
namespace linker {
+class OutputStream;
+
/**
* @class RelativePatcherTargetProvider
* @brief Interface for providing method offsets for relative call targets.
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index ca8743a..f7dbc1e 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -252,8 +252,8 @@
}
// Map method reference to assinged offset.
- // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
- class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider {
+ // Wrap the map in a class implementing RelativePatcherTargetProvider.
+ class MethodOffsetMap FINAL : public RelativePatcherTargetProvider {
public:
std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE {
auto it = map.find(ref);
diff --git a/compiler/linker/vector_output_stream.cc b/compiler/linker/vector_output_stream.cc
index f758005..75f90e5 100644
--- a/compiler/linker/vector_output_stream.cc
+++ b/compiler/linker/vector_output_stream.cc
@@ -19,6 +19,7 @@
#include "base/logging.h"
namespace art {
+namespace linker {
VectorOutputStream::VectorOutputStream(const std::string& location, std::vector<uint8_t>* vector)
: OutputStream(location), offset_(vector->size()), vector_(vector) {}
@@ -45,4 +46,5 @@
return offset_;
}
+} // namespace linker
} // namespace art
diff --git a/compiler/linker/vector_output_stream.h b/compiler/linker/vector_output_stream.h
index a9b93e7..92caf59 100644
--- a/compiler/linker/vector_output_stream.h
+++ b/compiler/linker/vector_output_stream.h
@@ -24,6 +24,7 @@
#include <vector>
namespace art {
+namespace linker {
class VectorOutputStream FINAL : public OutputStream {
public:
@@ -64,6 +65,7 @@
DISALLOW_COPY_AND_ASSIGN(VectorOutputStream);
};
+} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_VECTOR_OUTPUT_STREAM_H_
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
deleted file mode 100644
index 6f89049..0000000
--- a/compiler/oat_test.cc
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * 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 "elf_writer.h"
-#include "elf_writer_quick.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "linker/buffered_output_stream.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 {
-
-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);
- }
- linker::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 art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
deleted file mode 100644
index ce1b755..0000000
--- a/compiler/oat_writer.cc
+++ /dev/null
@@ -1,3632 +0,0 @@
-/*
- * 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 { // 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(linker::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 art
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
deleted file mode 100644
index ef0ce52..0000000
--- a/compiler/oat_writer.h
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * 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_COMPILER_OAT_WRITER_H_
-#define ART_COMPILER_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 linker::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 ImageWriter;
-class ProfileCompilationInfo;
-class OutputStream;
-class TimingLogger;
-class TypeLookupTable;
-class VdexFile;
-class ZipEntry;
-
-namespace debug {
-struct MethodDebugInfo;
-} // namespace debug
-
-namespace linker {
-class MultiOatRelativePatcher;
-} // namespace linker
-
-namespace verifier {
- class VerifierDeps;
-} // namespace verifier
-
-// 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(linker::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.
- linker::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 art
-
-#endif // ART_COMPILER_OAT_WRITER_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e128a15..8dd2762 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -63,7 +63,6 @@
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
-#include "elf_writer_quick.h"
#include "graph_checker.h"
#include "graph_visualizer.h"
#include "gvn.h"