Move .oat files to ELF format
Generates .oat in ELF file format using MCLinker
- Uses MCLinker IRBuilder to create a synthetic .o from OatWriter output.
- Uses new ElfFile for prelinking to support art image optimizations.
Adapted OatFile to load using dlopen, ElfFile, or memory, removing raw MemMap mechanism.
Changed image code to not assume oat data will be immediately after
image to allow space for ELF headers.
Passes test-art and works with installd.
Change-Id: Idc026eddb5de93f4b97490c405f3ed7b39589749
diff --git a/build/Android.common.mk b/build/Android.common.mk
index f2f38e1..a15a740 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -55,6 +55,7 @@
external/gtest/include \
external/valgrind/dynamic_annotations \
external/zlib \
+ frameworks/compile/mclinker/include \
art/src
art_cflags := \
@@ -67,6 +68,9 @@
-Wthread-safety \
-fstrict-aliasing
+# TODO: enable -std=gnu++0x for auto support when on Ubuntu 12.04 LTS (Precise Pangolin)
+# On 10.04 LTS (Lucid Lynx), it can cause dependencies on GLIBCXX_3.4.14 version symbols.
+
ifeq ($(HOST_OS),linux)
art_non_debug_cflags := \
-Wframe-larger-than=1728
@@ -159,6 +163,7 @@
src/disassembler_mips.cc \
src/disassembler_x86.cc \
src/dlmalloc.cc \
+ src/elf_file.cc \
src/file_output_stream.cc \
src/gc/card_table.cc \
src/gc/garbage_collector.cc \
@@ -378,6 +383,7 @@
src/dex_cache_test.cc \
src/dex_file_test.cc \
src/dex_instruction_visitor_test.cc \
+ src/elf_writer_test.cc \
src/exception_test.cc \
src/gc/space_bitmap_test.cc \
src/gc/space_test.cc \
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c3674f2..5f518a4 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -72,6 +72,7 @@
LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libstlport libz libcutils
LOCAL_STATIC_LIBRARIES += libgtest
LOCAL_MODULE_PATH := $(ART_NATIVETEST_OUT)
+ include $(LLVM_DEVICE_BUILD_MK)
include $(BUILD_EXECUTABLE)
art_gtest_exe := $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE)
ART_TARGET_TEST_EXECUTABLES += $$(art_gtest_exe)
@@ -83,6 +84,7 @@
# Mac OS complains about unresolved symbols if you don't include this.
LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_host
endif
+ include $(LLVM_HOST_BUILD_MK)
include $(BUILD_HOST_EXECUTABLE)
art_gtest_exe := $(HOST_OUT_EXECUTABLES)/$$(LOCAL_MODULE)
ART_HOST_TEST_EXECUTABLES += $$(art_gtest_exe)
diff --git a/build/Android.libart-compiler-llvm.mk b/build/Android.libart-compiler-llvm.mk
index 4909f81..c8dda8a 100644
--- a/build/Android.libart-compiler-llvm.mk
+++ b/build/Android.libart-compiler-llvm.mk
@@ -21,6 +21,8 @@
endif
LIBART_COMPILER_LLVM_SRC_FILES += \
+ src/elf_writer.cc \
+ src/compiler/write_elf.cc \
src/compiler_llvm/compilation_unit.cc \
src/compiler_llvm/compiler_llvm.cc \
src/compiler_llvm/generated/art_module.cc \
diff --git a/build/Android.libart-compiler.mk b/build/Android.libart-compiler.mk
index 829dd6a..339f591 100644
--- a/build/Android.libart-compiler.mk
+++ b/build/Android.libart-compiler.mk
@@ -15,6 +15,8 @@
#
LIBART_COMPILER_SRC_FILES += \
+ src/elf_writer.cc \
+ src/compiler/write_elf.cc \
src/compiler/dataflow.cc \
src/compiler/frontend.cc \
src/compiler/ralloc.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 6fc6a3d..c43c397 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -39,6 +39,7 @@
#include "intern_table.h"
#include "interpreter/interpreter.h"
#include "leb128.h"
+#include "oat.h"
#include "oat_file.h"
#include "object.h"
#include "object_utils.h"
@@ -703,7 +704,7 @@
oat_filename += runtime->GetHostPrefix();
oat_filename += oat_location->ToModifiedUtf8();
runtime->GetHeap()->UnReserveOatFileAddressRange();
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatBegin());
+ OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin());
VLOG(startup) << "ClassLinker::OpenOat entering oat_filename=" << oat_filename;
if (oat_file == NULL) {
LOG(ERROR) << "Failed to open oat file " << oat_filename << " referenced from image.";
@@ -751,8 +752,8 @@
if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) {
return NULL;
}
- if (oat_file->GetOatHeader().GetImageFileLocationOatBegin()
- != reinterpret_cast<uint32_t>(image_header.GetOatBegin())) {
+ if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
+ != reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) {
return NULL;
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location);
@@ -803,7 +804,7 @@
LOG(ERROR) << "Failed to seek to start of generated oat file: " << oat_location;
return NULL;
}
- const OatFile* oat_file = OatFile::Open(*file.get(), oat_location, NULL);
+ const OatFile* oat_file = OatFile::Open(file.get(), oat_location, NULL, false);
if (oat_file == NULL) {
LOG(ERROR) << "Failed to open generated oat file: " << oat_location;
return NULL;
@@ -823,9 +824,9 @@
Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader();
uint32_t image_oat_checksum = image_header.GetOatChecksum();
- uint32_t image_oat_begin = reinterpret_cast<uint32_t>(image_header.GetOatBegin());
+ uint32_t image_oat_data_begin = reinterpret_cast<uint32_t>(image_header.GetOatDataBegin());
bool image_check = ((oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum)
- && (oat_file->GetOatHeader().GetImageFileLocationOatBegin() == image_oat_begin));
+ && (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() == image_oat_data_begin));
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location);
if (oat_dex_file == NULL) {
@@ -850,9 +851,9 @@
ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8());
LOG(WARNING) << ".oat file " << oat_file->GetLocation()
<< " mismatch ( " << std::hex << oat_file->GetOatHeader().GetImageFileLocationOatChecksum()
- << ", " << oat_file->GetOatHeader().GetImageFileLocationOatBegin()
+ << ", " << oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
<< ") with " << image_file
- << " (" << image_oat_checksum << ", " << std::hex << image_oat_begin << ")";
+ << " (" << image_oat_checksum << ", " << std::hex << image_oat_data_begin << ")";
}
if (!dex_check) {
LOG(WARNING) << ".oat file " << oat_file->GetLocation()
@@ -968,7 +969,7 @@
OatFile* oat_file = OpenOat(space);
CHECK(oat_file != NULL) << "Failed to open oat file for image";
CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
- CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatBegin(), 0U);
+ CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
CHECK(oat_file->GetOatHeader().GetImageFileLocation().empty());
Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
ObjectArray<DexCache>* dex_caches = dex_caches_object->AsObjectArray<DexCache>();
@@ -987,6 +988,7 @@
SirtRef<DexCache> dex_cache(self, dex_caches->Get(i));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location);
+ CHECK(oat_dex_file != NULL) << oat_file->GetLocation() << " " << dex_file_location;
const DexFile* dex_file = oat_dex_file->OpenDexFile();
if (dex_file == NULL) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
diff --git a/src/common_test.h b/src/common_test.h
index 2836706..5a5479a 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -526,6 +526,18 @@
CompileMethod(method);
}
+ void ReserveImageSpace() {
+ // Reserve where the image will be loaded up front so that other parts of test set up don't
+ // accidentally end up colliding with the fixed memory address when we need to load the image.
+ image_reservation_.reset(MemMap::MapAnonymous("Image reservation", (byte*)ART_BASE_ADDRESS,
+ (size_t)100 * 1024 * 1024, // 100MB
+ PROT_NONE));
+ }
+
+ void UnreserveImageSpace() {
+ image_reservation_.reset();
+ }
+
std::string android_data_;
std::string art_cache_;
const DexFile* java_lang_dex_file_; // owned by runtime_
@@ -538,6 +550,7 @@
private:
std::vector<const DexFile*> opened_dex_files_;
+ UniquePtr<MemMap> image_reservation_;
};
// Sets a CheckJni abort hook to catch failures. Note that this will cause CheckJNI to carry on
diff --git a/src/compiler.cc b/src/compiler.cc
index 1988212..18460ce 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -1781,4 +1781,61 @@
return freezing_constructor_classes_.count(ClassReference(dex_file, class_def_index)) != 0;
}
+bool Compiler::WriteElf(std::vector<uint8_t>& oat_contents, File* file) {
+ typedef bool (*WriteElfFn)(Compiler&, std::vector<uint8_t>&, File*);
+ WriteElfFn WriteElf =
+ FindFunction<WriteElfFn>(MakeCompilerSoName(compiler_backend_), compiler_library_, "WriteElf");
+ return WriteElf(*this, oat_contents, file);
+}
+
+bool Compiler::FixupElf(File* file, uintptr_t oat_data_begin) const {
+ typedef bool (*FixupElfFn)(File*, uintptr_t oat_data_begin);
+ FixupElfFn FixupElf =
+ FindFunction<FixupElfFn>(MakeCompilerSoName(compiler_backend_), compiler_library_, "FixupElf");
+ return FixupElf(file, oat_data_begin);
+}
+
+void Compiler::GetOatElfInformation(File* file,
+ size_t& oat_loaded_size,
+ size_t& oat_data_offset) const {
+ typedef bool (*GetOatElfInformationFn)(File*, size_t& oat_loaded_size, size_t& oat_data_offset);
+ GetOatElfInformationFn GetOatElfInformation =
+ FindFunction<GetOatElfInformationFn>(MakeCompilerSoName(compiler_backend_), compiler_library_,
+ "GetOatElfInformation");
+ GetOatElfInformation(file, oat_loaded_size, oat_data_offset);
+}
+
+void Compiler::InstructionSetToLLVMTarget(InstructionSet instruction_set,
+ std::string& target_triple,
+ std::string& target_cpu,
+ std::string& target_attr) {
+ switch (instruction_set) {
+ case kThumb2:
+ target_triple = "thumb-none-linux-gnueabi";
+ target_cpu = "cortex-a9";
+ target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db";
+ break;
+
+ case kArm:
+ target_triple = "armv7-none-linux-gnueabi";
+ // TODO: Fix for Nexus S.
+ target_cpu = "cortex-a9";
+ // TODO: Fix for Xoom.
+ target_attr = "+v7,+neon,+neonfp,+vfp3,+db";
+ break;
+
+ case kX86:
+ target_triple = "i386-pc-linux-gnu";
+ target_attr = "";
+ break;
+
+ case kMips:
+ target_triple = "mipsel-unknown-linux";
+ target_attr = "mips32r2";
+ break;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << instruction_set;
+ }
+ }
} // namespace art
diff --git a/src/compiler.h b/src/compiler.h
index ad2a254..13130d7 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -188,6 +188,17 @@
void SetBitcodeFileName(std::string const& filename);
+ // TODO: remove when libart links against LLVM (when separate compiler library is gone)
+ bool WriteElf(std::vector<uint8_t>& oat_contents, File* file);
+ bool FixupElf(File* file, uintptr_t oat_data_begin) const;
+ void GetOatElfInformation(File* file, size_t& oat_loaded_size, size_t& oat_data_offset) const;
+
+ // TODO: move to a common home for llvm helpers once quick/portable are merged
+ static void InstructionSetToLLVMTarget(InstructionSet instruction_set,
+ std::string& target_triple,
+ std::string& target_cpu,
+ std::string& target_attr);
+
void SetCompilerContext(void* compiler_context) {
compiler_context_ = compiler_context;
}
diff --git a/src/compiler/write_elf.cc b/src/compiler/write_elf.cc
new file mode 100644
index 0000000..1fd8a9492
--- /dev/null
+++ b/src/compiler/write_elf.cc
@@ -0,0 +1,33 @@
+/*
+ * 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 "compiler.h"
+#include "elf_writer.h"
+#include "os.h"
+
+extern "C" bool WriteElf(art::Compiler& compiler,
+ std::vector<uint8_t>& oat_contents,
+ art::File* file) {
+ return art::ElfWriter::Create(file, oat_contents, compiler);
+}
+extern "C" bool FixupElf(art::File* file, uintptr_t oat_data_begin) {
+ return art::ElfWriter::Fixup(file, oat_data_begin);
+}
+extern "C" void GetOatElfInformation(art::File* file,
+ size_t& oat_loaded_size,
+ size_t& oat_data_offset) {
+ art::ElfWriter::GetOatElfInformation(file, oat_loaded_size, oat_data_offset);
+}
diff --git a/src/compiler_llvm/compilation_unit.cc b/src/compiler_llvm/compilation_unit.cc
index 78bc50d..bd2d8fc 100644
--- a/src/compiler_llvm/compilation_unit.cc
+++ b/src/compiler_llvm/compilation_unit.cc
@@ -178,39 +178,10 @@
bool CompilationUnit::MaterializeToRawOStream(llvm::raw_ostream& out_stream) {
// Lookup the LLVM target
- const char* target_triple = NULL;
- const char* target_cpu = "";
- const char* target_attr = NULL;
-
- InstructionSet insn_set = GetInstructionSet();
- switch (insn_set) {
- case kThumb2:
- target_triple = "thumb-none-linux-gnueabi";
- target_cpu = "cortex-a9";
- target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db";
- break;
-
- case kArm:
- target_triple = "armv7-none-linux-gnueabi";
- // TODO: Fix for Nexus S.
- target_cpu = "cortex-a9";
- // TODO: Fix for Xoom.
- target_attr = "+v7,+neon,+neonfp,+vfp3,+db";
- break;
-
- case kX86:
- target_triple = "i386-pc-linux-gnu";
- target_attr = "";
- break;
-
- case kMips:
- target_triple = "mipsel-unknown-linux";
- target_attr = "mips32r2";
- break;
-
- default:
- LOG(FATAL) << "Unknown instruction set: " << insn_set;
- }
+ std::string target_triple;
+ std::string target_cpu;
+ std::string target_attr;
+ Compiler::InstructionSetToLLVMTarget(GetInstructionSet(), target_triple, target_cpu, target_attr);
std::string errmsg;
const llvm::Target* target =
@@ -324,7 +295,6 @@
return true;
}
-
bool CompilationUnit::ExtractCodeAndPrelink(const std::string& elf_image) {
if (GetInstructionSet() == kX86) {
compiled_code_.push_back(0xccU);
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 82815d6..a2d35e4 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -29,7 +29,6 @@
#include "class_linker.h"
#include "class_loader.h"
#include "compiler.h"
-#include "file_output_stream.h"
#include "image_writer.h"
#include "leb128.h"
#include "oat_writer.h"
@@ -40,6 +39,7 @@
#include "scoped_thread_state_change.h"
#include "sirt_ref.h"
#include "timing_logger.h"
+#include "vector_output_stream.h"
#include "well_known_classes.h"
#include "zip_archive.h"
@@ -257,28 +257,35 @@
std::string image_file_location;
uint32_t image_file_location_oat_checksum = 0;
- uint32_t image_file_location_oat_begin = 0;
+ uint32_t image_file_location_oat_data_begin = 0;
Heap* heap = Runtime::Current()->GetHeap();
if (heap->GetSpaces().size() > 1) {
ImageSpace* image_space = heap->GetImageSpace();
image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
- image_file_location_oat_begin = reinterpret_cast<uint32_t>(image_space->GetImageHeader().GetOatBegin());
+ image_file_location_oat_data_begin = reinterpret_cast<uint32_t>(image_space->GetImageHeader().GetOatDataBegin());
image_file_location = image_space->GetImageFilename();
if (host_prefix != NULL && StartsWith(image_file_location, host_prefix->c_str())) {
image_file_location = image_file_location.substr(host_prefix->size());
}
}
- FileOutputStream file_output_stream(oat_file);
- if (!OatWriter::Create(file_output_stream,
+ std::vector<uint8_t> oat_contents;
+ VectorOutputStream vector_output_stream(oat_file->GetPath(), oat_contents);
+ if (!OatWriter::Create(vector_output_stream,
dex_files,
image_file_location_oat_checksum,
- image_file_location_oat_begin,
+ image_file_location_oat_data_begin,
image_file_location,
*compiler.get())) {
LOG(ERROR) << "Failed to create oat file " << oat_file->GetPath();
return NULL;
}
+
+ if (!compiler->WriteElf(oat_contents, oat_file)) {
+ LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
+ return NULL;
+ }
+
return compiler.release();
}
@@ -289,11 +296,29 @@
const std::string& oat_location,
const Compiler& compiler)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
- ImageWriter image_writer(image_classes);
- if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location, compiler)) {
- LOG(ERROR) << "Failed to create image file " << image_filename;
+ uintptr_t oat_data_begin;
+ {
+ // ImageWriter is scoped so it can free memory before doing FixupElf
+ ImageWriter image_writer(image_classes);
+ if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location, compiler)) {
+ LOG(ERROR) << "Failed to create image file " << image_filename;
+ return false;
+ }
+ oat_data_begin = image_writer.GetOatDataBegin();
+ }
+
+ LG << "dex2oat CreateImageFile opening : " << oat_filename;
+ UniquePtr<File> oat_file(OS::OpenFile(oat_filename.c_str(), true, false));
+ LG << "dex2oat CreateImageFile opened : " << oat_filename;
+ if (oat_file.get() == NULL) {
+ PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
return false;
}
+ if (!compiler.FixupElf(oat_file.get(), oat_data_begin)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ return false;
+ }
+ LOG(ERROR) << "ELF file fixed up successfully: " << oat_file->GetPath();
return true;
}
@@ -901,6 +926,56 @@
return EXIT_SUCCESS;
}
+ // Notes on the interleaving of creating the image and oat file to
+ // ensure the references between the two are correct.
+ //
+ // Currently we have a memory layout that looks something like this:
+ //
+ // +--------------+
+ // | image |
+ // +--------------+
+ // | boot oat |
+ // +--------------+
+ // | alloc spaces |
+ // +--------------+
+ //
+ // There are several constraints on the loading of the imag and boot.oat.
+ //
+ // 1. The image is expected to be loaded at an absolute address and
+ // contains Objects with absolute pointers within the image.
+ //
+ // 2. There are absolute pointers from Methods in the image to their
+ // code in the oat.
+ //
+ // 3. There are absolute pointers from the code in the oat to Methods
+ // in the image.
+ //
+ // 4. There are absolute pointers from code in the oat to other code
+ // in the oat.
+ //
+ // To get this all correct, we go through several steps.
+ //
+ // 1. We have already created that oat file above with
+ // CreateOatFile. Originally this was just our own proprietary file
+ // but now it is contained within an ELF dynamic object (aka .so
+ // file). The Compiler returned by CreateOatFile provides
+ // PatchInformation for references to oat code and Methods that need
+ // to be update once we know where the oat file will be located
+ // after the image.
+ //
+ // 2. We create the image file. It needs to know where the oat file
+ // will be loaded after itself. Originally when oat file was simply
+ // memory mapped so we could predict where its contents were based
+ // on the file size. Now that it is an ELF file, we need to inspect
+ // the ELF file to understand the in memory segment layout including
+ // where the oat header is located within. ImageWriter's
+ // PatchOatCodeAndMethods uses the PatchInformation from the
+ // Compiler to touch up absolute references in the oat file.
+ //
+ // 3. We fixup the ELF program headers so that dlopen will try to
+ // load the .so at the desired location at runtime by offsetting the
+ // Elf32_Phdr.p_vaddr values by the desired base address.
+ //
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
bool image_creation_success = dex2oat->CreateImageFile(image_filename,
image_base,
diff --git a/src/elf_file.cc b/src/elf_file.cc
new file mode 100644
index 0000000..8d6e630
--- /dev/null
+++ b/src/elf_file.cc
@@ -0,0 +1,559 @@
+/*
+ * 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_file.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "utils.h"
+
+namespace art {
+
+ElfFile::ElfFile() :
+ file_(NULL),
+ writable_(false),
+ program_header_only_(false),
+ header_(NULL),
+ base_address_(NULL),
+ program_headers_start_(NULL),
+ section_headers_start_(NULL),
+ dynamic_program_header_(NULL),
+ dynamic_section_start_(NULL),
+ symtab_section_start_(NULL),
+ dynsym_section_start_(NULL),
+ strtab_section_start_(NULL),
+ dynstr_section_start_(NULL),
+ hash_section_start_(NULL) {}
+
+ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only) {
+ UniquePtr<ElfFile> elf_file(new ElfFile());
+ if (!elf_file->Setup(file, writable, program_header_only)) {
+ return NULL;
+ }
+ return elf_file.release();
+}
+
+bool ElfFile::Setup(File* file, bool writable, bool program_header_only) {
+ CHECK(file != NULL);
+ file_ = file;
+ writable_ = writable;
+ program_header_only_ = program_header_only;
+
+ int prot;
+ int flags;
+ if (writable_) {
+ prot = PROT_READ | PROT_WRITE;
+ flags = MAP_SHARED;
+ } else {
+ prot = PROT_READ;
+ flags = MAP_PRIVATE;
+ }
+ if (file->GetLength() < sizeof(llvm::ELF::Elf32_Ehdr)) {
+ LOG(WARNING) << "File not large enough to contain ELF header: " << file->GetPath();
+ return false;
+ }
+
+ if (program_header_only) {
+ // first just map ELF header to get program header size information
+ size_t elf_header_size = sizeof(llvm::ELF::Elf32_Ehdr);
+ if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file->Fd(), 0))) {
+ LOG(WARNING) << "Failed to map ELF header: " << file->GetPath();
+ return false;
+ }
+ // then remap to cover program header
+ size_t program_header_size = header_->e_phoff + (header_->e_phentsize * header_->e_phnum);
+ if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file->Fd(), 0))) {
+ LOG(WARNING) << "Failed to map ELF program headers: " << file->GetPath();
+ return false;
+ }
+ } else {
+ // otherwise map entire file
+ if (!SetMap(MemMap::MapFile(file->GetLength(), prot, flags, file->Fd(), 0))) {
+ LOG(WARNING) << "Failed to map ELF file: " << file->GetPath();
+ return false;
+ }
+ }
+
+ // Either way, the program header is relative to the elf header
+ program_headers_start_ = Begin() + GetHeader().e_phoff;
+
+ if (!program_header_only) {
+ // Setup section headers.
+ section_headers_start_ = Begin() + GetHeader().e_shoff;
+
+ // Find .dynamic section info from program header
+ dynamic_program_header_ = FindProgamHeaderByType(llvm::ELF::PT_DYNAMIC);
+ if (dynamic_program_header_ == NULL) {
+ LOG(WARNING) << "Failed to find PT_DYNAMIC program header in ELF file: " << file->GetPath();
+ return false;
+ }
+
+ dynamic_section_start_
+ = reinterpret_cast<llvm::ELF::Elf32_Dyn*>(Begin() + GetDynamicProgramHeader().p_offset);
+
+ // Find other sections from section headers
+ for (llvm::ELF::Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) {
+ llvm::ELF::Elf32_Shdr& section_header = GetSectionHeader(i);
+ byte* section_addr = Begin() + section_header.sh_offset;
+ switch (section_header.sh_type) {
+ case llvm::ELF::SHT_SYMTAB: {
+ symtab_section_start_ = reinterpret_cast<llvm::ELF::Elf32_Sym*>(section_addr);
+ break;
+ }
+ case llvm::ELF::SHT_DYNSYM: {
+ dynsym_section_start_ = reinterpret_cast<llvm::ELF::Elf32_Sym*>(section_addr);
+ break;
+ }
+ case llvm::ELF::SHT_STRTAB: {
+ // TODO: base these off of sh_link from .symtab and .dynsym above
+ if ((section_header.sh_flags & llvm::ELF::SHF_ALLOC) != 0) {
+ dynstr_section_start_ = reinterpret_cast<char*>(section_addr);
+ } else {
+ strtab_section_start_ = reinterpret_cast<char*>(section_addr);
+ }
+ break;
+ }
+ case llvm::ELF::SHT_DYNAMIC: {
+ if (reinterpret_cast<byte*>(dynamic_section_start_) != section_addr) {
+ LOG(WARNING) << "Failed to find matching SHT_DYNAMIC for PT_DYNAMIC in "
+ << file->GetPath() << ": " << std::hex
+ << reinterpret_cast<void*>(dynamic_section_start_)
+ << " != " << reinterpret_cast<void*>(section_addr);
+ return false;
+ }
+ break;
+ }
+ case llvm::ELF::SHT_HASH: {
+ hash_section_start_ = reinterpret_cast<llvm::ELF::Elf32_Word*>(section_addr);
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+ElfFile::~ElfFile() {
+ STLDeleteElements(&segments_);
+}
+
+bool ElfFile::SetMap(MemMap* map) {
+ if (map == NULL) {
+ // MemMap::Open should have already logged
+ return false;
+ }
+ map_.reset(map);
+ CHECK(map_.get() != NULL) << file_->GetPath();
+ CHECK(map_->Begin() != NULL) << file_->GetPath();
+
+ header_ = reinterpret_cast<llvm::ELF::Elf32_Ehdr*>(map_->Begin());
+ if ((llvm::ELF::ElfMagic[0] != header_->e_ident[llvm::ELF::EI_MAG0])
+ || (llvm::ELF::ElfMagic[1] != header_->e_ident[llvm::ELF::EI_MAG1])
+ || (llvm::ELF::ElfMagic[2] != header_->e_ident[llvm::ELF::EI_MAG2])
+ || (llvm::ELF::ElfMagic[3] != header_->e_ident[llvm::ELF::EI_MAG3])) {
+ LOG(WARNING) << "Failed to find ELF magic in " << file_->GetPath()
+ << ": " << std::hex
+ << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG0])
+ << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG1])
+ << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG2])
+ << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG3]);
+ return false;
+ }
+
+
+ // TODO: remove these static_casts from enum when using -std=gnu++0x
+ CHECK_EQ(static_cast<unsigned char>(llvm::ELF::ELFCLASS32), header_->e_ident[llvm::ELF::EI_CLASS]) << file_->GetPath();
+ CHECK_EQ(static_cast<unsigned char>(llvm::ELF::ELFDATA2LSB), header_->e_ident[llvm::ELF::EI_DATA]) << file_->GetPath();
+ CHECK_EQ(static_cast<unsigned char>(llvm::ELF::EV_CURRENT), header_->e_ident[llvm::ELF::EI_VERSION]) << file_->GetPath();
+
+ // TODO: remove these static_casts from enum when using -std=gnu++0x
+ CHECK_EQ(static_cast<llvm::ELF::Elf32_Half>(llvm::ELF::ET_DYN), header_->e_type) << file_->GetPath();
+ CHECK_EQ(static_cast<llvm::ELF::Elf32_Word>(llvm::ELF::EV_CURRENT), header_->e_version) << file_->GetPath();
+ CHECK_EQ(0U, header_->e_entry) << file_->GetPath();
+
+ CHECK_NE(0U, header_->e_phoff) << file_->GetPath();
+ CHECK_NE(0U, header_->e_shoff) << file_->GetPath();
+ CHECK_NE(0U, header_->e_ehsize) << file_->GetPath();
+ CHECK_NE(0U, header_->e_phentsize) << file_->GetPath();
+ CHECK_NE(0U, header_->e_phnum) << file_->GetPath();
+ CHECK_NE(0U, header_->e_shentsize) << file_->GetPath();
+ CHECK_NE(0U, header_->e_shnum) << file_->GetPath();
+ CHECK_NE(0U, header_->e_shstrndx) << file_->GetPath();
+ CHECK_GE(header_->e_shnum, header_->e_shstrndx) << file_->GetPath();
+ if (!program_header_only_) {
+ CHECK_GT(Size(), header_->e_phoff) << file_->GetPath();
+ CHECK_GT(Size(), header_->e_shoff) << file_->GetPath();
+ }
+ return true;
+}
+
+
+llvm::ELF::Elf32_Ehdr& ElfFile::GetHeader() {
+ CHECK(header_ != NULL);
+ return *header_;
+}
+
+byte* ElfFile::GetProgramHeadersStart() {
+ CHECK(program_headers_start_ != NULL);
+ return program_headers_start_;
+}
+
+byte* ElfFile::GetSectionHeadersStart() {
+ CHECK(section_headers_start_ != NULL);
+ return section_headers_start_;
+}
+
+llvm::ELF::Elf32_Phdr& ElfFile::GetDynamicProgramHeader() {
+ CHECK(dynamic_program_header_ != NULL);
+ return *dynamic_program_header_;
+}
+
+llvm::ELF::Elf32_Dyn* ElfFile::GetDynamicSectionStart() {
+ CHECK(dynamic_section_start_ != NULL);
+ return dynamic_section_start_;
+}
+
+llvm::ELF::Elf32_Sym* ElfFile::GetSymbolSectionStart(llvm::ELF::Elf32_Word section_type) {
+ CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
+ llvm::ELF::Elf32_Sym* symbol_section_start;
+ switch (section_type) {
+ case llvm::ELF::SHT_SYMTAB: {
+ symbol_section_start = symtab_section_start_;
+ break;
+ }
+ case llvm::ELF::SHT_DYNSYM: {
+ symbol_section_start = dynsym_section_start_;
+ break;
+ }
+ default: {
+ LOG(FATAL) << section_type;
+ symbol_section_start = NULL;
+ }
+ }
+ CHECK(symbol_section_start != NULL);
+ return symbol_section_start;
+}
+
+char* ElfFile::GetSymbolStringSectionStart(llvm::ELF::Elf32_Word section_type) {
+ CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
+ char* string_section_start;
+ switch (section_type) {
+ case llvm::ELF::SHT_SYMTAB: {
+ string_section_start = strtab_section_start_;
+ break;
+ }
+ case llvm::ELF::SHT_DYNSYM: {
+ string_section_start = dynstr_section_start_;
+ break;
+ }
+ default: {
+ LOG(FATAL) << section_type;
+ string_section_start = NULL;
+ }
+ }
+ CHECK(string_section_start != NULL);
+ return string_section_start;
+}
+
+llvm::ELF::Elf32_Word* ElfFile::GetHashSectionStart() {
+ CHECK(hash_section_start_ != NULL);
+ return hash_section_start_;
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetHashBucketNum() {
+ return GetHashSectionStart()[0];
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetHashChainNum() {
+ return GetHashSectionStart()[1];
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetHashBucket(size_t i) {
+ CHECK_LT(i, GetHashBucketNum());
+ // 0 is nbucket, 1 is nchain
+ return GetHashSectionStart()[2 + i];
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetHashChain(size_t i) {
+ CHECK_LT(i, GetHashChainNum());
+ // 0 is nbucket, 1 is nchain, & chains are after buckets
+ return GetHashSectionStart()[2 + GetHashBucketNum() + i];
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetProgramHeaderNum() {
+ return GetHeader().e_phnum;
+}
+
+llvm::ELF::Elf32_Phdr& ElfFile::GetProgramHeader(llvm::ELF::Elf32_Word i) {
+ CHECK_LT(i, GetProgramHeaderNum()) << file_->GetPath();
+ byte* program_header = GetProgramHeadersStart() + (i * GetHeader().e_phentsize);
+ CHECK_LT(program_header, End()) << file_->GetPath();
+ return *reinterpret_cast<llvm::ELF::Elf32_Phdr*>(program_header);
+}
+
+llvm::ELF::Elf32_Phdr* ElfFile::FindProgamHeaderByType(llvm::ELF::Elf32_Word type) {
+ for (llvm::ELF::Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
+ llvm::ELF::Elf32_Phdr& program_header = GetProgramHeader(i);
+ if (program_header.p_type == type) {
+ return &program_header;
+ }
+ }
+ return NULL;
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetSectionHeaderNum() {
+ return GetHeader().e_shnum;
+}
+
+llvm::ELF::Elf32_Shdr& ElfFile::GetSectionHeader(llvm::ELF::Elf32_Word i) {
+ // Can only access arbitrary sections when we have the whole file, not just program header.
+ // Even if we Load(), it doesn't bring in all the sections.
+ CHECK(!program_header_only_) << file_->GetPath();
+ CHECK_LT(i, GetSectionHeaderNum()) << file_->GetPath();
+ byte* section_header = GetSectionHeadersStart() + (i * GetHeader().e_shentsize);
+ CHECK_LT(section_header, End()) << file_->GetPath();
+ return *reinterpret_cast<llvm::ELF::Elf32_Shdr*>(section_header);
+}
+
+llvm::ELF::Elf32_Shdr* ElfFile::FindSectionByType(llvm::ELF::Elf32_Word type) {
+ // Can only access arbitrary sections when we have the whole file, not just program header.
+ // We could change this to switch on known types if they were detected during loading.
+ CHECK(!program_header_only_) << file_->GetPath();
+ for (llvm::ELF::Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) {
+ llvm::ELF::Elf32_Shdr& section_header = GetSectionHeader(i);
+ if (section_header.sh_type == type) {
+ return §ion_header;
+ }
+ }
+ return NULL;
+}
+
+// from bionic
+static unsigned elfhash(const char *_name)
+{
+ const unsigned char *name = (const unsigned char *) _name;
+ unsigned h = 0, g;
+
+ while(*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+ return h;
+}
+
+byte* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) {
+ llvm::ELF::Elf32_Word hash = elfhash(symbol_name.c_str());
+ llvm::ELF::Elf32_Word bucket_index = hash % GetHashBucketNum();
+ llvm::ELF::Elf32_Word symbol_and_chain_index = GetHashBucket(bucket_index);
+ char* symbol_string_section_start = GetSymbolStringSectionStart(llvm::ELF::SHT_DYNSYM);
+ while (symbol_and_chain_index != 0 /* STN_UNDEF */) {
+ llvm::ELF::Elf32_Sym& symbol = GetSymbol(llvm::ELF::SHT_DYNSYM, symbol_and_chain_index);
+ char* name = symbol_string_section_start + symbol.st_name;
+ if (symbol_name == name) {
+ return base_address_ + symbol.st_value;
+ }
+ symbol_and_chain_index = GetHashChain(symbol_and_chain_index);
+ }
+ return NULL;
+}
+
+bool ElfFile::IsSymbolSectionType(llvm::ELF::Elf32_Word section_type) {
+ return ((section_type == llvm::ELF::SHT_SYMTAB) || (section_type == llvm::ELF::SHT_DYNSYM));
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetSymbolNum(llvm::ELF::Elf32_Shdr& section_header) {
+ CHECK(IsSymbolSectionType(section_header.sh_type)) << file_->GetPath() << " " << section_header.sh_type;
+ CHECK_NE(0U, section_header.sh_entsize) << file_->GetPath();
+ return section_header.sh_size / section_header.sh_entsize;
+}
+
+llvm::ELF::Elf32_Sym& ElfFile::GetSymbol(llvm::ELF::Elf32_Word section_type,
+ llvm::ELF::Elf32_Word i) {
+ return *(GetSymbolSectionStart(section_type) + i);
+}
+
+llvm::ELF::Elf32_Sym* ElfFile::FindSymbolByName(llvm::ELF::Elf32_Word section_type,
+ const std::string& symbol_name) {
+ CHECK(!program_header_only_) << file_->GetPath();
+ CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type;
+ llvm::ELF::Elf32_Shdr* symbol_section = FindSectionByType(section_type);
+ CHECK(symbol_section != NULL) << file_->GetPath();
+ llvm::ELF::Elf32_Shdr& string_section = GetSectionHeader(symbol_section->sh_link);
+ for (uint32_t i = 0; i < GetSymbolNum(*symbol_section); i++) {
+ llvm::ELF::Elf32_Sym& symbol = GetSymbol(section_type, i);
+ const char* name = GetString(string_section, symbol.st_name);
+ if (name == NULL) {
+ continue;
+ }
+ if (symbol_name == name) {
+ return &symbol;
+ }
+ }
+ return NULL;
+}
+
+llvm::ELF::Elf32_Addr ElfFile::FindSymbolAddress(llvm::ELF::Elf32_Word section_type,
+ const std::string& symbol_name) {
+ llvm::ELF::Elf32_Sym* symbol = FindSymbolByName(section_type, symbol_name);
+ if (symbol == NULL) {
+ return 0;
+ }
+ return symbol->st_value;
+}
+
+char* ElfFile::GetString(llvm::ELF::Elf32_Shdr& string_section, llvm::ELF::Elf32_Word i) {
+ CHECK(!program_header_only_) << file_->GetPath();
+ // TODO: remove this static_cast from enum when using -std=gnu++0x
+ CHECK_EQ(static_cast<llvm::ELF::Elf32_Word>(llvm::ELF::SHT_STRTAB), string_section.sh_type) << file_->GetPath();
+ CHECK_LT(i, string_section.sh_size) << file_->GetPath();
+ if (i == 0) {
+ return NULL;
+ }
+ byte* strings = Begin() + string_section.sh_offset;
+ byte* string = strings + i;
+ CHECK_LT(string, End()) << file_->GetPath();
+ return reinterpret_cast<char*>(string);
+}
+
+llvm::ELF::Elf32_Word ElfFile::GetDynamicNum() {
+ return GetDynamicProgramHeader().p_filesz / sizeof(llvm::ELF::Elf32_Dyn);
+}
+
+llvm::ELF::Elf32_Dyn& ElfFile::GetDynamic(llvm::ELF::Elf32_Word i) {
+ CHECK_LT(i, GetDynamicNum()) << file_->GetPath();
+ return *(GetDynamicSectionStart() + i);
+}
+
+// Base on bionic phdr_table_get_load_size
+size_t ElfFile::GetLoadedSize() {
+ llvm::ELF::Elf32_Addr min_vaddr = 0xFFFFFFFFu;
+ llvm::ELF::Elf32_Addr max_vaddr = 0x00000000u;
+ for (llvm::ELF::Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
+ llvm::ELF::Elf32_Phdr& program_header = GetProgramHeader(i);
+ if (program_header.p_type != llvm::ELF::PT_LOAD) {
+ continue;
+ }
+ llvm::ELF::Elf32_Addr begin_vaddr = program_header.p_vaddr;
+ if (begin_vaddr < min_vaddr) {
+ min_vaddr = begin_vaddr;
+ }
+ llvm::ELF::Elf32_Addr end_vaddr = program_header.p_vaddr + program_header.p_memsz;
+ if (end_vaddr > max_vaddr) {
+ max_vaddr = end_vaddr;
+ }
+ }
+ min_vaddr = RoundDown(min_vaddr, kPageSize);
+ max_vaddr = RoundUp(max_vaddr, kPageSize);
+ CHECK_LT(min_vaddr, max_vaddr) << file_->GetPath();
+ size_t loaded_size = max_vaddr - min_vaddr;
+ return loaded_size;
+}
+
+bool ElfFile::Load() {
+ // TODO: actually return false error
+ CHECK(program_header_only_) << file_->GetPath();
+ for (llvm::ELF::Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
+ llvm::ELF::Elf32_Phdr& program_header = GetProgramHeader(i);
+
+ // Record .dynamic header information for later use
+ if (program_header.p_type == llvm::ELF::PT_DYNAMIC) {
+ dynamic_program_header_ = &program_header;
+ continue;
+ }
+
+ // Not something to load, move on.
+ if (program_header.p_type != llvm::ELF::PT_LOAD) {
+ continue;
+ }
+
+ // Found something to load.
+
+ // If p_vaddr is zero, it must be the first loadable segment,
+ // since they must be in order. Since it is zero, there isn't a
+ // specific address requested, so first request a contiguous chunk
+ // of required size for all segments, but with no
+ // permissions. We'll then carve that up with the proper
+ // permissions as we load the actual segments. If p_vaddr is
+ // non-zero, the segments require the specific address specified,
+ // which either was specified in the file because we already set
+ // base_address_ after the first zero segment).
+ if (program_header.p_vaddr == 0) {
+ std::string reservation_name("ElfFile reservation for ");
+ reservation_name += file_->GetPath();
+ UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
+ NULL, GetLoadedSize(), PROT_NONE));
+ CHECK(reserve.get() != NULL) << file_->GetPath();
+ base_address_ = reserve->Begin();
+ segments_.push_back(reserve.release());
+ }
+ byte* p_vaddr = base_address_ + program_header.p_vaddr;
+ int prot = 0;
+ if ((program_header.p_flags & llvm::ELF::PF_X) != 0) {
+ prot |= PROT_EXEC;
+ }
+ if ((program_header.p_flags & llvm::ELF::PF_W) != 0) {
+ prot |= PROT_WRITE;
+ }
+ if ((program_header.p_flags & llvm::ELF::PF_R) != 0) {
+ prot |= PROT_READ;
+ }
+ int flags = MAP_FIXED;
+ if (writable_) {
+ prot |= PROT_WRITE;
+ flags |= MAP_SHARED;
+ } else {
+ flags |= MAP_PRIVATE;
+ }
+ UniquePtr<MemMap> segment(MemMap::MapFileAtAddress(p_vaddr,
+ program_header.p_memsz,
+ prot, flags, file_->Fd(),
+ program_header.p_offset,
+ true));
+ CHECK(segment.get() != NULL) << file_->GetPath();
+ CHECK_EQ(segment->Begin(), p_vaddr) << file_->GetPath();
+ segments_.push_back(segment.release());
+ }
+ // Now that we are done loading, .dynamic should be in memory to find .dynstr, .dynsym, .hash
+ dynamic_section_start_
+ = reinterpret_cast<llvm::ELF::Elf32_Dyn*>(base_address_ + GetDynamicProgramHeader().p_vaddr);
+ for (llvm::ELF::Elf32_Word i = 0; i < GetDynamicNum(); i++) {
+ llvm::ELF::Elf32_Dyn& elf_dyn = GetDynamic(i);
+ byte* d_ptr = base_address_ + elf_dyn.d_un.d_ptr;
+ switch (elf_dyn.d_tag) {
+ case llvm::ELF::DT_HASH: {
+ hash_section_start_ = reinterpret_cast<llvm::ELF::Elf32_Word*>(d_ptr);
+ break;
+ }
+ case llvm::ELF::DT_STRTAB: {
+ dynstr_section_start_ = reinterpret_cast<char*>(d_ptr);
+ break;
+ }
+ case llvm::ELF::DT_SYMTAB: {
+ dynsym_section_start_ = reinterpret_cast<llvm::ELF::Elf32_Sym*>(d_ptr);
+ break;
+ }
+ case llvm::ELF::DT_NULL: {
+ CHECK_EQ(GetDynamicNum(), i+1);
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace art
diff --git a/src/elf_file.h b/src/elf_file.h
new file mode 100644
index 0000000..2c49f32
--- /dev/null
+++ b/src/elf_file.h
@@ -0,0 +1,133 @@
+/*
+ * 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_SRC_ELF_FILE_H_
+#define ART_SRC_ELF_FILE_H_
+
+#include <vector>
+
+#include <llvm/Support/ELF.h>
+
+#include "base/unix_file/fd_file.h"
+#include "globals.h"
+#include "mem_map.h"
+#include "os.h"
+#include "UniquePtr.h"
+
+namespace art {
+
+// Used for compile time and runtime for ElfFile access. Because of
+// the need for use at runtime, cannot directly use LLVM classes such as
+// ELFObjectFile.
+class ElfFile {
+ public:
+ static ElfFile* Open(File* file, bool writable, bool program_header_only);
+ ~ElfFile();
+
+ // Load segments into memory based on PT_LOAD program headers
+
+ File& GetFile() const {
+ return *file_;
+ }
+
+ byte* Begin() {
+ return map_->Begin();
+ }
+
+ byte* End() {
+ return map_->End();
+ }
+
+ size_t Size() const {
+ return map_->Size();
+ }
+
+ llvm::ELF::Elf32_Ehdr& GetHeader();
+
+ llvm::ELF::Elf32_Word GetProgramHeaderNum();
+ llvm::ELF::Elf32_Phdr& GetProgramHeader(llvm::ELF::Elf32_Word);
+ llvm::ELF::Elf32_Phdr* FindProgamHeaderByType(llvm::ELF::Elf32_Word type);
+
+ llvm::ELF::Elf32_Word GetSectionHeaderNum();
+ llvm::ELF::Elf32_Shdr& GetSectionHeader(llvm::ELF::Elf32_Word);
+ llvm::ELF::Elf32_Shdr* FindSectionByType(llvm::ELF::Elf32_Word type);
+
+ byte* FindDynamicSymbolAddress(const std::string& symbol_name);
+
+ static bool IsSymbolSectionType(llvm::ELF::Elf32_Word section_type);
+ llvm::ELF::Elf32_Word GetSymbolNum(llvm::ELF::Elf32_Shdr&);
+ llvm::ELF::Elf32_Sym& GetSymbol(llvm::ELF::Elf32_Word section_type, llvm::ELF::Elf32_Word i);
+ llvm::ELF::Elf32_Sym* FindSymbolByName(llvm::ELF::Elf32_Word section_type,
+ const std::string& symbol_name);
+ llvm::ELF::Elf32_Addr FindSymbolAddress(llvm::ELF::Elf32_Word section_type,
+ const std::string& symbol_name);
+
+ char* GetString(llvm::ELF::Elf32_Shdr&, llvm::ELF::Elf32_Word);
+
+ llvm::ELF::Elf32_Word GetDynamicNum();
+ llvm::ELF::Elf32_Dyn& GetDynamic(llvm::ELF::Elf32_Word);
+
+ // Returns the expected size when the file is loaded at runtime
+ size_t GetLoadedSize();
+
+ // Load segments into memory based on PT_LOAD program headers
+ bool Load();
+
+ private:
+ ElfFile();
+
+ bool Setup(File* file, bool writable, bool program_header_only);
+
+ bool SetMap(MemMap* map);
+
+ byte* GetProgramHeadersStart();
+ byte* GetSectionHeadersStart();
+ llvm::ELF::Elf32_Phdr& GetDynamicProgramHeader();
+ llvm::ELF::Elf32_Dyn* GetDynamicSectionStart();
+ llvm::ELF::Elf32_Sym* GetSymbolSectionStart(llvm::ELF::Elf32_Word section_type);
+ char* GetSymbolStringSectionStart(llvm::ELF::Elf32_Word section_type);
+ llvm::ELF::Elf32_Word* GetHashSectionStart();
+ llvm::ELF::Elf32_Word GetHashBucketNum();
+ llvm::ELF::Elf32_Word GetHashChainNum();
+ llvm::ELF::Elf32_Word GetHashBucket(size_t i);
+ llvm::ELF::Elf32_Word GetHashChain(size_t i);
+
+ File* file_;
+ bool writable_;
+ bool program_header_only_;
+ UniquePtr<MemMap> map_;
+ llvm::ELF::Elf32_Ehdr* header_;
+ std::vector<MemMap*> segments_;
+ byte* base_address_;
+
+ // The program header should always available but use GetProgramHeadersStart() to be sure.
+ byte* program_headers_start_;
+
+ // Conditionally available values. Use accessors to ensure they exist if they are required.
+ byte* section_headers_start_;
+ llvm::ELF::Elf32_Phdr* dynamic_program_header_;
+ llvm::ELF::Elf32_Dyn* dynamic_section_start_;
+ llvm::ELF::Elf32_Sym* symtab_section_start_;
+ llvm::ELF::Elf32_Sym* dynsym_section_start_;
+ char* strtab_section_start_;
+ char* dynstr_section_start_;
+ llvm::ELF::Elf32_Word* hash_section_start_;
+
+};
+
+} // namespace art
+
+#endif // ART_SRC_ELF_FILE_H_
diff --git a/src/elf_writer.cc b/src/elf_writer.cc
new file mode 100644
index 0000000..d4f91d6
--- /dev/null
+++ b/src/elf_writer.cc
@@ -0,0 +1,318 @@
+/*
+ * 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"
+#include "oat.h"
+#include "oat_file.h"
+
+#include <llvm/Support/TargetSelect.h>
+
+#include <mcld/Environment.h>
+#include <mcld/IRBuilder.h>
+#include <mcld/Linker.h>
+#include <mcld/LinkerConfig.h>
+#include <mcld/Module.h>
+#include <mcld/Support/Path.h>
+#include <mcld/Support/TargetSelect.h>
+
+namespace art {
+
+bool ElfWriter::Create(File* file, std::vector<uint8_t>& oat_contents, const Compiler& compiler) {
+ ElfWriter elf_writer(compiler);
+ return elf_writer.Write(oat_contents, file);
+}
+
+ElfWriter::ElfWriter(const Compiler& compiler) : compiler_(&compiler) {}
+
+ElfWriter::~ElfWriter() {}
+
+static void InitializeLLVM() {
+ // TODO: this is lifted from art's compiler_llvm.cc, should be factored out
+#if defined(ART_TARGET)
+ llvm::InitializeNativeTarget();
+ // TODO: odd that there is no InitializeNativeTargetMC?
+#else
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllTargetMCs();
+#endif
+}
+
+bool ElfWriter::Write(std::vector<uint8_t>& oat_contents, File* elf_file) {
+
+ std::string target_triple;
+ std::string target_cpu;
+ std::string target_attr;
+ Compiler::InstructionSetToLLVMTarget(compiler_->GetInstructionSet(),
+ target_triple,
+ target_cpu,
+ target_attr);
+
+ {
+ // Based on mclinker's llvm-mcld.cpp main() and LinkerTest
+ //
+ // TODO: LinkerTest uses mcld::Initialize(), but it does an
+ // llvm::InitializeAllTargets, which we don't want. Basically we
+ // want mcld::InitializeNative, but it doesn't exist yet, so we
+ // inline the minimal we need here.
+ InitializeLLVM();
+ mcld::InitializeAllTargets();
+ mcld::InitializeAllLinkers();
+ mcld::InitializeAllEmulations();
+ mcld::InitializeAllDiagnostics();
+
+ UniquePtr<mcld::LinkerConfig> linker_config(new mcld::LinkerConfig(target_triple));
+ CHECK(linker_config.get() != NULL);
+ linker_config->setCodeGenType(mcld::LinkerConfig::DynObj);
+ linker_config->options().setSOName(elf_file->GetPath());
+ // TODO: Wire up mcld DiagnosticEngine to LOG?
+ if (false) {
+ // enables some tracing of input file processing
+ linker_config->options().setTrace(true);
+ }
+
+ // Based on alone::Linker::config
+ UniquePtr<mcld::Module> module(new mcld::Module(linker_config->options().soname()));
+ CHECK(module.get() != NULL);
+ UniquePtr<mcld::IRBuilder> ir_builder(new mcld::IRBuilder(*module.get(), *linker_config.get()));
+ CHECK(ir_builder.get() != NULL);
+ UniquePtr<mcld::Linker> linker(new mcld::Linker());
+ CHECK(linker.get() != NULL);
+ linker->config(*linker_config.get());
+
+
+ // Add an artificial memory input. Based on LinkerTest.
+ UniquePtr<OatFile> oat_file(OatFile::Open(oat_contents, elf_file->GetPath()));
+ CHECK(oat_file.get() != NULL) << elf_file->GetPath();
+
+ const char* oat_data_start = reinterpret_cast<const char*>(&oat_file->GetOatHeader());
+ const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset();
+ const char* oat_code_start = oat_data_start + oat_data_length;
+ const size_t oat_code_length = oat_file->Size() - oat_data_length;
+
+ // TODO: ownership of input?
+ mcld::Input* input = ir_builder->CreateInput("oat contents",
+ mcld::sys::fs::Path("oat contents path"),
+ mcld::Input::Object);
+ CHECK(input != NULL);
+
+ // TODO: ownership of null_section?
+ mcld::LDSection* null_section = ir_builder->CreateELFHeader(*input,
+ "",
+ mcld::LDFileFormat::Null,
+ llvm::ELF::SHT_NULL,
+ 0);
+ CHECK(null_section != NULL);
+
+ // TODO: we should split readonly data from readonly executable
+ // code like .oat does. We need to control section layout with
+ // linker script like functionality to guarantee references
+ // between sections maintain relative position which isn't
+ // possible right now with the mclinker APIs.
+ CHECK(oat_code_start);
+
+ // TODO: ownership of text_section?
+ // we need to ensure that oatdata is page aligned so when we
+ // fixup the segment load addresses, they remain page aligned.
+ mcld::LDSection* text_section = ir_builder->CreateELFHeader(*input,
+ ".text",
+ llvm::ELF::SHT_PROGBITS,
+ llvm::ELF::SHF_EXECINSTR
+ | llvm::ELF::SHF_ALLOC,
+ kPageSize);
+ CHECK(text_section != NULL);
+
+ mcld::SectionData* text_section_data = ir_builder->CreateSectionData(*text_section);
+ CHECK(text_section_data != NULL);
+
+ // TODO: why does IRBuilder::CreateRegion take a non-const pointer?
+ mcld::Fragment* text_fragment = ir_builder->CreateRegion(const_cast<char*>(oat_data_start),
+ oat_file->Size());
+ CHECK(text_fragment != NULL);
+ ir_builder->AppendFragment(*text_fragment, *text_section_data);
+
+ ir_builder->AddSymbol(*input,
+ "oatdata",
+ mcld::ResolveInfo::Object,
+ mcld::ResolveInfo::Define,
+ mcld::ResolveInfo::Global,
+ oat_data_length, // size
+ 0, // offset
+ text_section);
+
+ ir_builder->AddSymbol(*input,
+ "oatexec",
+ mcld::ResolveInfo::Object,
+ mcld::ResolveInfo::Define,
+ mcld::ResolveInfo::Global,
+ oat_code_length, // size
+ oat_data_length, // offset
+ text_section);
+
+ ir_builder->AddSymbol(*input,
+ "oatlastword",
+ mcld::ResolveInfo::Object,
+ mcld::ResolveInfo::Define,
+ mcld::ResolveInfo::Global,
+ 0, // size
+ // subtract a word so symbol is within section
+ (oat_data_length + oat_code_length) - sizeof(uint32_t), // offset
+ text_section);
+
+ // link inputs
+ if (!linker->link(*module.get(), *ir_builder.get())) {
+ LOG(ERROR) << "problem linking " << elf_file->GetPath();
+ return false;
+ }
+
+ // emited linked output
+ if (!linker->emit(elf_file->Fd())) {
+ LOG(ERROR) << "problem emitting " << elf_file->GetPath();
+ return false;
+ }
+ // TODO: mcld::Linker::emit closed the file descriptor. It probably shouldn't.
+ // For now, close our File to match.
+ elf_file->Close();
+ mcld::Finalize();
+ }
+ LOG(INFO) << "ELF file written successfully: " << elf_file->GetPath();
+ return true;
+}
+
+bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
+ CHECK(elf_file.get() != NULL);
+
+ // Lookup "oatdata" symbol address.
+ llvm::ELF::Elf32_Addr oatdata_address = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM,
+ "oatdata");
+ CHECK_NE(0U, oatdata_address);
+ llvm::ELF::Elf32_Off base_address = oat_data_begin - oatdata_address;
+
+ if (!FixupDynamic(*elf_file.get(), base_address)) {
+ LOG(WARNING) << "Failed fo fixup .dynamic in " << file->GetPath();
+ return false;
+ }
+ if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
+ LOG(WARNING) << "Failed fo fixup section headers in " << file->GetPath();
+ return false;
+ }
+ if (!FixupProgramHeaders(*elf_file.get(), base_address)) {
+ LOG(WARNING) << "Failed fo fixup program headers in " << file->GetPath();
+ return false;
+ }
+ if (!FixupSymbols(*elf_file.get(), base_address, true)) {
+ LOG(WARNING) << "Failed fo fixup .dynsym in " << file->GetPath();
+ return false;
+ }
+ if (!FixupSymbols(*elf_file.get(), base_address, false)) {
+ LOG(WARNING) << "Failed fo fixup .symtab in " << file->GetPath();
+ return false;
+ }
+ return true;
+}
+
+bool ElfWriter::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) {
+ // TODO: C++0x auto.
+ for (llvm::ELF::Elf32_Word i = 0; i < elf_file.GetDynamicNum(); i++) {
+ llvm::ELF::Elf32_Dyn& elf_dyn = elf_file.GetDynamic(i);
+ bool elf_dyn_needs_fixup = false;
+ // case 1: if Elf32_Dyn.d_tag implies Elf32_Dyn.d_un contains an address in d_ptr
+ switch (elf_dyn.d_tag) {
+ case llvm::ELF::DT_PLTGOT:
+ case llvm::ELF::DT_HASH:
+ case llvm::ELF::DT_STRTAB:
+ case llvm::ELF::DT_SYMTAB:
+ case llvm::ELF::DT_RELA:
+ case llvm::ELF::DT_INIT:
+ case llvm::ELF::DT_FINI:
+ case llvm::ELF::DT_REL:
+ case llvm::ELF::DT_DEBUG:
+ case llvm::ELF::DT_JMPREL: {
+ elf_dyn_needs_fixup = true;
+ break;
+ }
+ default: {
+ // case 2: if d_tag is even and greater than > DT_ENCODING
+ if ((elf_dyn.d_tag > llvm::ELF::DT_ENCODING) && ((elf_dyn.d_tag % 2) == 0)) {
+ elf_dyn_needs_fixup = true;
+ }
+ break;
+ }
+ }
+ if (elf_dyn_needs_fixup) {
+ uint32_t d_ptr = elf_dyn.d_un.d_ptr;
+ d_ptr += base_address;
+ elf_dyn.d_un.d_ptr = d_ptr;
+ }
+ }
+ return true;
+}
+
+bool ElfWriter::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) {
+ for (llvm::ELF::Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) {
+ llvm::ELF::Elf32_Shdr& sh = elf_file.GetSectionHeader(i);
+ // 0 implies that the section will not exist in the memory of the process
+ if (sh.sh_addr == 0) {
+ continue;
+ }
+ sh.sh_addr += base_address;
+ }
+ return true;
+}
+
+bool ElfWriter::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) {
+ // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now.
+ for (llvm::ELF::Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) {
+ llvm::ELF::Elf32_Phdr& ph = elf_file.GetProgramHeader(i);
+ CHECK_EQ(ph.p_vaddr, ph.p_paddr) << elf_file.GetFile().GetPath() << " i=" << i;
+ CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))));
+ ph.p_vaddr += base_address;
+ ph.p_paddr += base_address;
+ CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1))));
+ }
+ return true;
+}
+
+bool ElfWriter::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic) {
+ llvm::ELF::Elf32_Word section_type = dynamic ? llvm::ELF::SHT_DYNSYM : llvm::ELF::SHT_SYMTAB;
+ // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
+ llvm::ELF::Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type);
+ CHECK(symbol_section != NULL) << elf_file.GetFile().GetPath();
+ for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) {
+ llvm::ELF::Elf32_Sym& symbol = elf_file.GetSymbol(section_type, i);
+ if (symbol.st_value != 0) {
+ symbol.st_value += base_address;
+ }
+ }
+ return true;
+}
+
+void ElfWriter::GetOatElfInformation(File* file,
+ size_t& oat_loaded_size,
+ size_t& oat_data_offset) {
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, false, false));
+ CHECK(elf_file.get() != NULL);
+
+ oat_loaded_size = elf_file->GetLoadedSize();
+ CHECK_NE(0U, oat_loaded_size);
+ oat_data_offset = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM, "oatdata");
+ CHECK_NE(0U, oat_data_offset);
+}
+
+} // namespace art
diff --git a/src/elf_writer.h b/src/elf_writer.h
new file mode 100644
index 0000000..0402c40
--- /dev/null
+++ b/src/elf_writer.h
@@ -0,0 +1,63 @@
+/*
+ * 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_SRC_ELF_WRITER_H_
+#define ART_SRC_ELF_WRITER_H_
+
+#include "compiler.h"
+#include "os.h"
+
+namespace art {
+
+class ElfWriter {
+ public:
+ // Write an ELF file. Returns true on success, false on failure.
+ static bool Create(File* file, std::vector<uint8_t>& oat_contents, const Compiler& compiler);
+
+ // Fixup an ELF file so that that oat header will be loaded at oat_begin.
+ // Returns true on success, false on failure.
+ static bool Fixup(File* file, uintptr_t oat_data_begin);
+
+ // 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);
+
+ private:
+ ElfWriter(const Compiler& compiler);
+ ~ElfWriter();
+
+ bool Write(std::vector<uint8_t>& oat_contents, File* elf_file);
+
+ // Fixup .dynamic d_ptr values for the expected base_address.
+ static bool FixupDynamic(ElfFile& elf_file, uintptr_t base_address);
+
+ // Fixup Elf32_Shdr p_vaddr to load at the desired address.
+ static bool FixupSectionHeaders(ElfFile& elf_file,uintptr_t base_address);
+
+ // Fixup Elf32_Phdr p_vaddr to load at the desired address.
+ static bool FixupProgramHeaders(ElfFile& elf_file,uintptr_t base_address);
+
+ // Fixup symbol table
+ static bool FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic);
+
+ const Compiler* compiler_;
+};
+
+} // namespace art
+
+#endif // ART_SRC_ELF_WRITER_H_
diff --git a/src/elf_writer_test.cc b/src/elf_writer_test.cc
new file mode 100644
index 0000000..57d7e14
--- /dev/null
+++ b/src/elf_writer_test.cc
@@ -0,0 +1,86 @@
+/*
+ * 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 "common_test.h"
+
+#include "oat.h"
+#include "elf_file.h"
+
+namespace art {
+
+class ElfWriterTest : public CommonTest {
+
+ protected:
+ virtual void SetUp() {
+ ReserveImageSpace();
+ CommonTest::SetUp();
+ }
+};
+
+#define EXPECT_ELF_FILE_ADDRESS(ef, value, name) \
+ EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(llvm::ELF::SHT_SYMTAB, name))); \
+ EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(llvm::ELF::SHT_DYNSYM, name))); \
+ EXPECT_EQ(value, ef->FindDynamicSymbolAddress(name)); \
+
+TEST_F(ElfWriterTest, dlsym) {
+ std::string elf_filename;
+ if (IsHost()) {
+ const char* host_dir = getenv("ANDROID_HOST_OUT");
+ CHECK(host_dir != NULL);
+ elf_filename = StringPrintf("%s/framework/core.oat", host_dir);
+ } else {
+ elf_filename = "/data/art-test/core.oat";
+ }
+ LOG(INFO) << "elf_filename=" << elf_filename;
+
+ UnreserveImageSpace();
+ void* dl_oat_so = dlopen(elf_filename.c_str(), RTLD_NOW);
+ ASSERT_TRUE(dl_oat_so != NULL) << dlerror();
+ void* dl_oatdata = dlsym(dl_oat_so, "oatdata");
+ ASSERT_TRUE(dl_oatdata != NULL);
+
+ OatHeader* dl_oat_header = reinterpret_cast<OatHeader*>(dl_oatdata);
+ ASSERT_TRUE(dl_oat_header->IsValid());
+ void* dl_oatexec = dlsym(dl_oat_so, "oatexec");
+ ASSERT_TRUE(dl_oatexec != NULL);
+ ASSERT_LT(dl_oatdata, dl_oatexec);
+
+ void* dl_oatlastword = dlsym(dl_oat_so, "oatlastword");
+ ASSERT_TRUE(dl_oatlastword != NULL);
+ ASSERT_LT(dl_oatexec, dl_oatlastword);
+
+ ASSERT_EQ(0, dlclose(dl_oat_so));
+
+ UniquePtr<File> file(OS::OpenFile(elf_filename.c_str(), false));
+ ASSERT_TRUE(file.get() != NULL);
+ {
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false));
+ CHECK(ef.get() != NULL);
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata");
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec");
+ EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword");
+ }
+ {
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, true));
+ CHECK(ef.get() != NULL);
+ ef->Load();
+ EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata"));
+ EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec"));
+ EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword"));
+ }
+}
+
+} // namespace art
diff --git a/src/gc/space.cc b/src/gc/space.cc
index 7e6f7ed..04f932d 100644
--- a/src/gc/space.cc
+++ b/src/gc/space.cc
@@ -494,7 +494,8 @@
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_FIXED,
file->Fd(),
- 0));
+ 0,
+ false));
if (map.get() == NULL) {
LOG(ERROR) << "Failed to map " << image_file_name;
return NULL;
diff --git a/src/heap.cc b/src/heap.cc
index 40037e3..805d63c 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -215,18 +215,18 @@
AddSpace(image_space);
// Oat files referenced by image files immediately follow them in memory, ensure alloc space
// isn't going to get in the middle
- byte* oat_end_addr = image_space->GetImageHeader().GetOatEnd();
- CHECK_GT(oat_end_addr, image_space->End());
+ byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
+ CHECK_GT(oat_file_end_addr, image_space->End());
// Reserve address range from image_space->End() to image_space->GetImageHeader().GetOatEnd()
uintptr_t reserve_begin = RoundUp(reinterpret_cast<uintptr_t>(image_space->End()), kPageSize);
- uintptr_t reserve_end = RoundUp(reinterpret_cast<uintptr_t>(oat_end_addr), kPageSize);
+ uintptr_t reserve_end = RoundUp(reinterpret_cast<uintptr_t>(oat_file_end_addr), kPageSize);
oat_file_map_.reset(MemMap::MapAnonymous("oat file reserve",
reinterpret_cast<byte*>(reserve_begin),
reserve_end - reserve_begin, PROT_NONE));
- if (oat_end_addr > requested_begin) {
- requested_begin = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_end_addr),
+ if (oat_file_end_addr > requested_begin) {
+ requested_begin = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_file_end_addr),
kPageSize));
}
}
diff --git a/src/image.cc b/src/image.cc
index 3ce27aa..a190f10 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -19,6 +19,6 @@
namespace art {
const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' };
+const byte ImageHeader::kImageVersion[] = { '0', '0', '2', '\0' };
} // namespace art
diff --git a/src/image.h b/src/image.h
index 9ed4dc1..f38f04b 100644
--- a/src/image.h
+++ b/src/image.h
@@ -32,18 +32,25 @@
ImageHeader(uint32_t image_begin,
uint32_t image_roots,
uint32_t oat_checksum,
- uint32_t oat_begin,
- uint32_t oat_end)
+ uint32_t oat_file_begin,
+ uint32_t oat_data_begin,
+ uint32_t oat_data_end,
+ uint32_t oat_file_end)
: image_begin_(image_begin),
oat_checksum_(oat_checksum),
- oat_begin_(oat_begin),
- oat_end_(oat_end),
+ oat_file_begin_(oat_file_begin),
+ oat_data_begin_(oat_data_begin),
+ oat_data_end_(oat_data_end),
+ oat_file_end_(oat_file_end),
image_roots_(image_roots) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
- CHECK_EQ(oat_begin, RoundUp(oat_begin, kPageSize));
+ CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
+ CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
CHECK_LT(image_begin, image_roots);
- CHECK_LT(image_roots, oat_begin);
- CHECK_LT(oat_begin, oat_end);
+ CHECK_LT(image_roots, oat_file_begin);
+ CHECK_LE(oat_file_begin, oat_data_begin);
+ CHECK_LT(oat_data_begin, oat_data_end);
+ CHECK_LE(oat_data_end, oat_file_end);
memcpy(magic_, kImageMagic, sizeof(kImageMagic));
memcpy(version_, kImageVersion, sizeof(kImageVersion));
}
@@ -75,12 +82,20 @@
oat_checksum_ = oat_checksum;
}
- byte* GetOatBegin() const {
- return reinterpret_cast<byte*>(oat_begin_);
+ byte* GetOatFileBegin() const {
+ return reinterpret_cast<byte*>(oat_file_begin_);
}
- byte* GetOatEnd() const {
- return reinterpret_cast<byte*>(oat_end_);
+ byte* GetOatDataBegin() const {
+ return reinterpret_cast<byte*>(oat_data_begin_);
+ }
+
+ byte* GetOatDataEnd() const {
+ return reinterpret_cast<byte*>(oat_data_end_);
+ }
+
+ byte* GetOatFileEnd() const {
+ return reinterpret_cast<byte*>(oat_file_end_);
}
enum ImageRoot {
@@ -114,19 +129,26 @@
byte magic_[4];
byte version_[4];
- // required base address for mapping the image.
+ // Required base address for mapping the image.
uint32_t image_begin_;
- // checksum of the oat file we link to for load time sanity check
+ // Checksum of the oat file we link to for load time sanity check.
uint32_t oat_checksum_;
- // required oat address expected by image Method::GetCode() pointers.
- uint32_t oat_begin_;
+ // Start address for oat file. Will be before oat_data_begin_ for .so files.
+ uint32_t oat_file_begin_;
- // end of oat address range for this image file, used for positioning a following image
- uint32_t oat_end_;
+ // Required oat address expected by image Method::GetCode() pointers.
+ uint32_t oat_data_begin_;
- // absolute address of an Object[] of objects needed to reinitialize from an image
+ // End of oat data address range for this image file.
+ uint32_t oat_data_end_;
+
+ // End of oat file address range. will be after oat_data_end_ for
+ // .so files. Used for positioning a following alloc spaces.
+ uint32_t oat_file_end_;
+
+ // Absolute address of an Object[] of objects needed to reinitialize from an image.
uint32_t image_roots_;
friend class ImageWriter;
diff --git a/src/image_test.cc b/src/image_test.cc
index 502e1a0..89e3a05 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -20,12 +20,12 @@
#include "common_test.h"
#include "image.h"
#include "image_writer.h"
-#include "file_output_stream.h"
#include "oat_writer.h"
#include "signal_catcher.h"
#include "gc/space.h"
#include "UniquePtr.h"
#include "utils.h"
+#include "vector_output_stream.h"
namespace art {
@@ -33,43 +33,48 @@
protected:
virtual void SetUp() {
- // Reserve where the image will be loaded up front so that other parts of test set up don't
- // accidentally end up colliding with the fixed memory address when we need to load the image.
- image_reservation_.reset(MemMap::MapAnonymous("Image reservation", (byte*)ART_BASE_ADDRESS,
- (size_t)100 * 1024 *1024, // 100MB
- PROT_NONE));
+ ReserveImageSpace();
CommonTest::SetUp();
}
- UniquePtr<MemMap> image_reservation_;
};
TEST_F(ImageTest, WriteRead) {
- ScratchFile tmp_oat;
+ ScratchFile tmp_elf;
{
- ScopedObjectAccess soa(Thread::Current());
- std::vector<const DexFile*> dex_files;
- dex_files.push_back(java_lang_dex_file_);
- FileOutputStream file_output_stream(tmp_oat.GetFile());
- bool success_oat = OatWriter::Create(file_output_stream, dex_files, 0, 0, "", *compiler_.get());
- ASSERT_TRUE(success_oat);
+ std::vector<uint8_t> oat_contents;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ std::vector<const DexFile*> dex_files;
+ dex_files.push_back(java_lang_dex_file_);
+ VectorOutputStream output_stream(tmp_elf.GetFilename(), oat_contents);
+ bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", *compiler_.get());
+ ASSERT_TRUE(success_oat);
- // Force all system classes into memory
- for (size_t i = 0; i < java_lang_dex_file_->NumClassDefs(); ++i) {
- const DexFile::ClassDef& class_def = java_lang_dex_file_->GetClassDef(i);
- const char* descriptor = java_lang_dex_file_->GetClassDescriptor(class_def);
- Class* klass = class_linker_->FindSystemClass(descriptor);
- EXPECT_TRUE(klass != NULL) << descriptor;
+ // Force all system classes into memory
+ for (size_t i = 0; i < java_lang_dex_file_->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = java_lang_dex_file_->GetClassDef(i);
+ const char* descriptor = java_lang_dex_file_->GetClassDescriptor(class_def);
+ Class* klass = class_linker_->FindSystemClass(descriptor);
+ EXPECT_TRUE(klass != NULL) << descriptor;
+ }
}
+ bool success_elf = compiler_->WriteElf(oat_contents, tmp_elf.GetFile());
+ ASSERT_TRUE(success_elf);
}
+ // Workound bug that mcld::Linker::emit closes tmp_elf by reopening as tmp_oat.
+ UniquePtr<File> tmp_oat(OS::OpenFile(tmp_elf.GetFilename().c_str(), true, false));
+ ASSERT_TRUE(tmp_oat.get() != NULL);
ScratchFile tmp_image;
const uintptr_t requested_image_base = ART_BASE_ADDRESS;
{
ImageWriter writer(NULL);
bool success_image = writer.Write(tmp_image.GetFilename(), requested_image_base,
- tmp_oat.GetFilename(), tmp_oat.GetFilename(),
+ tmp_oat->GetPath(), tmp_oat->GetPath(),
*compiler_.get());
ASSERT_TRUE(success_image);
+ bool success_fixup = compiler_->FixupElf(tmp_oat.get(), writer.GetOatDataBegin());
+ ASSERT_TRUE(success_fixup);
}
{
@@ -99,7 +104,7 @@
ASSERT_TRUE(dex.get() != NULL);
// Remove the reservation of the memory for use to load the image.
- image_reservation_.reset();
+ UnreserveImageSpace();
Runtime::Options options;
std::string image("-Ximage:");
diff --git a/src/image_writer.cc b/src/image_writer.cc
index 186cb6c..fc88cbb 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -33,6 +33,7 @@
#include "heap.h"
#include "image.h"
#include "intern_table.h"
+#include "oat.h"
#include "oat_file.h"
#include "object.h"
#include "object_utils.h"
@@ -64,11 +65,12 @@
dex_caches_.insert(dex_cache);
}
- oat_file_ = OatFile::Open(oat_filename, oat_location, NULL, true);
- if (oat_file_ == NULL) {
- LOG(ERROR) << "Failed to open oat file " << oat_filename;
+ UniquePtr<File> oat_file(OS::OpenFile(oat_filename.c_str(), true, false));
+ if (oat_file.get() == NULL) {
+ LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
return false;
}
+ oat_file_ = OatFile::Open(oat_file.get(), oat_location, NULL, true);
class_linker->RegisterOatFile(*oat_file_);
{
@@ -97,21 +99,24 @@
}
#endif
Thread::Current()->TransitionFromSuspendedToRunnable();
- CalculateNewObjectOffsets();
+ size_t oat_loaded_size = 0;
+ size_t oat_data_offset = 0;
+ compiler.GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset);
+ CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset);
CopyAndFixupObjects();
PatchOatCodeAndMethods(compiler);
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
- UniquePtr<File> file(OS::OpenFile(image_filename.c_str(), true));
- if (file.get() == NULL) {
+ UniquePtr<File> image_file(OS::OpenFile(image_filename.c_str(), true));
+ if (image_file.get() == NULL) {
LOG(ERROR) << "Failed to open image file " << image_filename;
return false;
}
- if (fchmod(file->Fd(), 0644) != 0) {
+ if (fchmod(image_file->Fd(), 0644) != 0) {
PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
return EXIT_FAILURE;
}
- bool success = file->WriteFully(image_->Begin(), image_end_);
+ bool success = image_file->WriteFully(image_->Begin(), image_end_);
if (!success) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
return false;
@@ -363,7 +368,8 @@
return image_roots.get();
}
-void ImageWriter::CalculateNewObjectOffsets() {
+void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) {
+ CHECK_NE(0U, oat_loaded_size);
Thread* self = Thread::Current();
SirtRef<ObjectArray<Object> > image_roots(self, CreateImageRoots());
@@ -390,17 +396,22 @@
self->EndAssertNoThreadSuspension(old);
}
- // Note that image_top_ is left at end of used space
- oat_begin_ = image_begin_ + RoundUp(image_end_, kPageSize);
- const byte* oat_limit = oat_begin_ + oat_file_->Size();
+ const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize);
+ const byte* oat_file_end = oat_file_begin + oat_loaded_size;
+ oat_data_begin_ = oat_file_begin + oat_data_offset;
+ const byte* oat_data_end = oat_data_begin_ + oat_file_->Size();
// return to write header at start of image with future location of image_roots
ImageHeader image_header(reinterpret_cast<uint32_t>(image_begin_),
reinterpret_cast<uint32_t>(GetImageAddress(image_roots.get())),
oat_file_->GetOatHeader().GetChecksum(),
- reinterpret_cast<uint32_t>(oat_begin_),
- reinterpret_cast<uint32_t>(oat_limit));
+ reinterpret_cast<uint32_t>(oat_file_begin),
+ reinterpret_cast<uint32_t>(oat_data_begin_),
+ reinterpret_cast<uint32_t>(oat_data_end),
+ reinterpret_cast<uint32_t>(oat_file_end));
memcpy(image_->Begin(), &image_header, sizeof(image_header));
+
+ // Note that image_end_ is left at end of used space
}
void ImageWriter::CopyAndFixupObjects()
diff --git a/src/image_writer.h b/src/image_writer.h
index 0a0854a..64bac2e 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -40,7 +40,7 @@
public:
explicit ImageWriter(const std::set<std::string>* image_classes)
: oat_file_(NULL), image_end_(0), image_begin_(NULL), image_classes_(image_classes),
- oat_begin_(NULL) {}
+ oat_data_begin_(NULL) {}
~ImageWriter() {}
@@ -51,6 +51,10 @@
const Compiler& compiler)
LOCKS_EXCLUDED(Locks::mutator_lock_);
+ uintptr_t GetOatDataBegin() {
+ return reinterpret_cast<uintptr_t>(oat_data_begin_);
+ }
+
private:
bool AllocMemory();
@@ -99,7 +103,7 @@
if (offset == 0) {
return NULL;
}
- return oat_begin_ + offset;
+ return oat_data_begin_ + offset;
}
bool IsImageClass(const Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -123,7 +127,8 @@
static void CheckNonImageClassesRemovedCallback(Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CalculateNewObjectOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ObjectArray<Object>* CreateImageRoots() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void CalculateNewObjectOffsetsCallback(Object* obj, void* arg)
@@ -170,7 +175,7 @@
const std::set<std::string>* image_classes_;
// Beginning target oat address for the pointers from the output image to its oat file
- const byte* oat_begin_;
+ const byte* oat_data_begin_;
// DexCaches seen while scanning for fixing up CodeAndDirectMethods
typedef std::set<DexCache*> Set;
diff --git a/src/mem_map.cc b/src/mem_map.cc
index a2ddf3c..32f0530 100644
--- a/src/mem_map.cc
+++ b/src/mem_map.cc
@@ -96,7 +96,8 @@
return new MemMap(name, actual, byte_count, actual, page_aligned_byte_count, prot);
}
-MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start) {
+MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count,
+ int prot, int flags, int fd, off_t start, bool reuse) {
CHECK_NE(0U, byte_count);
CHECK_NE(0, prot);
CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
@@ -104,8 +105,13 @@
int page_offset = start % kPageSize;
off_t page_aligned_offset = start - page_offset;
size_t page_aligned_byte_count = RoundUp(byte_count + page_offset, kPageSize);
- CheckMapRequest(addr, page_aligned_byte_count);
- byte* actual = reinterpret_cast<byte*>(mmap(addr,
+ byte* page_aligned_addr = addr - page_offset;
+ if (!reuse) {
+ // reuse means it is okay that it overlaps an existing page mapping.
+ // Only use this if you actually made the page reservation yourself.
+ CheckMapRequest(page_aligned_addr, page_aligned_byte_count);
+ }
+ byte* actual = reinterpret_cast<byte*>(mmap(page_aligned_addr,
page_aligned_byte_count,
prot,
flags,
@@ -114,7 +120,8 @@
if (actual == MAP_FAILED) {
std::string maps;
ReadFileToString("/proc/self/maps", &maps);
- PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_byte_count
+ PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(page_aligned_addr)
+ << ", " << page_aligned_byte_count
<< ", " << prot << ", " << flags << ", " << fd << ", " << page_aligned_offset
<< ") failed\n" << maps;
return NULL;
diff --git a/src/mem_map.h b/src/mem_map.h
index 3327a37..7310f78 100644
--- a/src/mem_map.h
+++ b/src/mem_map.h
@@ -45,7 +45,7 @@
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start) {
- return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start);
+ return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false);
}
// Map part of a file, taking care of non-page aligned offsets. The
@@ -54,7 +54,7 @@
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
static MemMap* MapFileAtAddress(
- byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start);
+ byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start, bool reuse);
// Releases the memory mapping
~MemMap();
diff --git a/src/native/dalvik_system_DexFile.cc b/src/native/dalvik_system_DexFile.cc
index a6a5c98..7485600 100644
--- a/src/native/dalvik_system_DexFile.cc
+++ b/src/native/dalvik_system_DexFile.cc
@@ -23,6 +23,7 @@
#include "gc/space.h"
#include "image.h"
#include "jni_internal.h"
+#include "oat.h"
#include "os.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
@@ -249,8 +250,8 @@
<< image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
return JNI_TRUE;
}
- if (oat_file->GetOatHeader().GetImageFileLocationOatBegin()
- != reinterpret_cast<uint32_t>(image_header.GetOatBegin())) {
+ if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
+ != reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) {
ScopedObjectAccess soa(env);
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " has out-of-date oat begin compared to "
diff --git a/src/oat.cc b/src/oat.cc
index a328a11..8032a11 100644
--- a/src/oat.cc
+++ b/src/oat.cc
@@ -30,7 +30,7 @@
OatHeader::OatHeader(InstructionSet instruction_set,
const std::vector<const DexFile*>* dex_files,
uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_begin,
+ uint32_t image_file_location_oat_data_begin,
const std::string& image_file_location) {
memcpy(magic_, kOatMagic, sizeof(kOatMagic));
memcpy(version_, kOatVersion, sizeof(kOatVersion));
@@ -47,9 +47,9 @@
image_file_location_oat_checksum_ = image_file_location_oat_checksum;
UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
- CHECK(IsAligned<kPageSize>(image_file_location_oat_begin));
- image_file_location_oat_begin_ = image_file_location_oat_begin;
- UpdateChecksum(&image_file_location_oat_begin_, sizeof(image_file_location_oat_begin_));
+ CHECK(IsAligned<kPageSize>(image_file_location_oat_data_begin));
+ image_file_location_oat_data_begin_ = image_file_location_oat_data_begin;
+ UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_));
image_file_location_size_ = image_file_location.size();
UpdateChecksum(&image_file_location_size_, sizeof(image_file_location_size_));
@@ -106,9 +106,9 @@
return image_file_location_oat_checksum_;
}
-uint32_t OatHeader::GetImageFileLocationOatBegin() const {
+uint32_t OatHeader::GetImageFileLocationOatDataBegin() const {
CHECK(IsValid());
- return image_file_location_oat_begin_;
+ return image_file_location_oat_data_begin_;
}
uint32_t OatHeader::GetImageFileLocationSize() const {
diff --git a/src/oat.h b/src/oat.h
index 1f12b54..1d2db6c 100644
--- a/src/oat.h
+++ b/src/oat.h
@@ -31,7 +31,7 @@
OatHeader(InstructionSet instruction_set,
const std::vector<const DexFile*>* dex_files,
uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_begin,
+ uint32_t image_file_location_oat_data_begin,
const std::string& image_file_location);
bool IsValid() const;
@@ -43,7 +43,7 @@
InstructionSet GetInstructionSet() const;
void SetExecutableOffset(uint32_t executable_offset);
uint32_t GetImageFileLocationOatChecksum() const;
- uint32_t GetImageFileLocationOatBegin() const;
+ uint32_t GetImageFileLocationOatDataBegin() const;
uint32_t GetImageFileLocationSize() const;
const uint8_t* GetImageFileLocationData() const;
std::string GetImageFileLocation() const;
@@ -61,7 +61,7 @@
uint32_t executable_offset_;
uint32_t image_file_location_oat_checksum_;
- uint32_t image_file_location_oat_begin_;
+ uint32_t image_file_location_oat_data_begin_;
uint32_t image_file_location_size_;
uint8_t image_file_location_data_[0]; // note variable width data at end
diff --git a/src/oat_file.cc b/src/oat_file.cc
index b201630..8229f63 100644
--- a/src/oat_file.cc
+++ b/src/oat_file.cc
@@ -16,8 +16,12 @@
#include "oat_file.h"
+#include <dlfcn.h>
+
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
+#include "elf_file.h"
+#include "oat.h"
#include "os.h"
namespace art {
@@ -29,28 +33,65 @@
return oat_location;
}
-OatFile* OatFile::Open(const std::string& filename,
- const std::string& location,
- byte* requested_base,
- bool writable) {
- CHECK(!filename.empty()) << location;
- UniquePtr<File> file(OS::OpenFile(filename.c_str(), writable, false));
- if (file.get() == NULL) {
- return NULL;
- }
- return Open(*file.get(), location, requested_base, writable);
-}
-
-OatFile* OatFile::Open(File& file,
- const std::string& location,
- byte* requested_base,
- bool writable) {
+void OatFile::CheckLocation(const std::string& location) {
CHECK(!location.empty());
if (!IsValidOatFilename(location)) {
LOG(WARNING) << "Attempting to open oat file with unknown extension '" << location << "'";
}
+}
+
+OatFile* OatFile::Open(std::vector<uint8_t>& oat_contents,
+ const std::string& location) {
+ CHECK(!oat_contents.empty()) << location;
+ CheckLocation(location);
UniquePtr<OatFile> oat_file(new OatFile(location));
- bool success = oat_file->Map(file, requested_base, writable);
+ oat_file->begin_ = &oat_contents[0];
+ oat_file->end_ = &oat_contents[oat_contents.size()];
+ oat_file->Setup();
+ return oat_file.release();
+}
+
+OatFile* OatFile::Open(const std::string& filename,
+ const std::string& location,
+ byte* requested_base) {
+ CHECK(!filename.empty()) << location;
+ CheckLocation(location);
+ OatFile* result = OpenDlopen(filename, location, requested_base);
+ if (result != NULL) {
+ return result;
+ }
+ UniquePtr<File> file(OS::OpenFile(filename.c_str(), false, false));
+ if (file.get() == NULL) {
+ return NULL;
+ }
+ return OpenElfFile(file.get(), location, requested_base, false);
+}
+
+OatFile* OatFile::Open(File* file,
+ const std::string& location,
+ byte* requested_base,
+ bool writable) {
+ CheckLocation(location);
+ return OpenElfFile(file, location, requested_base, writable);
+}
+
+OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
+ const std::string& location,
+ byte* requested_base) {
+ UniquePtr<OatFile> oat_file(new OatFile(location));
+ bool success = oat_file->Dlopen(elf_filename, requested_base);
+ if (!success) {
+ return NULL;
+ }
+ return oat_file.release();
+}
+
+OatFile* OatFile::OpenElfFile(File* file,
+ const std::string& location,
+ byte* requested_base,
+ bool writable) {
+ UniquePtr<OatFile> oat_file(new OatFile(location));
+ bool success = oat_file->ElfFileOpen(file, requested_base, writable);
if (!success) {
return NULL;
}
@@ -58,110 +99,133 @@
}
OatFile::OatFile(const std::string& location)
- : location_(location) {
+ : location_(location), begin_(NULL), end_(NULL), dlopen_handle_(NULL) {
CHECK(!location_.empty());
}
OatFile::~OatFile() {
STLDeleteValues(&oat_dex_files_);
+ if (dlopen_handle_ != NULL) {
+ dlclose(dlopen_handle_);
+ }
}
-bool OatFile::Map(File& file,
- byte* requested_base,
- bool writable) {
- OatHeader oat_header;
- bool success = file.ReadFully(&oat_header, sizeof(oat_header));
- if (!success || !oat_header.IsValid()) {
- LOG(WARNING) << "Invalid oat header " << GetLocation();
+bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base) {
+
+ char* absolute_path = realpath(elf_filename.c_str(), NULL);
+ if (absolute_path == NULL) {
+ PLOG(WARNING) << "Failed to create absolute path for " << elf_filename;
return false;
}
-
- int flags = 0;
- int prot = 0;
- if (writable) {
- flags |= MAP_SHARED; // So changes will write through to file
- prot |= (PROT_READ | PROT_WRITE);
- } else {
- flags |= MAP_PRIVATE;
- prot |= PROT_READ;
- }
- if (requested_base != NULL) {
- flags |= MAP_FIXED;
- }
- UniquePtr<MemMap> map(MemMap::MapFileAtAddress(requested_base,
- file.GetLength(),
- prot,
- flags,
- file.Fd(),
- 0));
- if (map.get() == NULL) {
- LOG(WARNING) << "Failed to map oat file from " << file.GetPath() << " for " << GetLocation();
+ dlopen_handle_ = dlopen(absolute_path, RTLD_NOW);
+ free(absolute_path);
+ if (dlopen_handle_ == NULL) {
return false;
}
- CHECK(requested_base == 0 || requested_base == map->Begin())
- << file.GetPath() << " for " << GetLocation() << " " << reinterpret_cast<void*>(map->Begin());
- DCHECK_EQ(0, memcmp(&oat_header, map->Begin(), sizeof(OatHeader)))
- << file.GetPath() << " for " << GetLocation();
-
- off_t code_offset = oat_header.GetExecutableOffset();
- if (code_offset < file.GetLength()) {
- byte* code_address = map->Begin() + code_offset;
- size_t code_length = file.GetLength() - code_offset;
- if (mprotect(code_address, code_length, prot | PROT_EXEC) != 0) {
- PLOG(ERROR) << "Failed to make oat code executable in "
- << file.GetPath() << " for " << GetLocation();
- return false;
- }
- } else {
- // its possible to have no code if all the methods were abstract, native, etc
- DCHECK_EQ(code_offset, RoundUp(file.GetLength(), kPageSize))
- << file.GetPath() << " for " << GetLocation();
+ begin_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatdata"));
+ if (begin_ == NULL) {
+ LOG(WARNING) << "Failed to find oatdata symbol in " << elf_filename << ": " << dlerror();
+ return false;
}
+ if (requested_base != NULL && begin_ != requested_base) {
+ std::string maps;
+ ReadFileToString("/proc/self/maps", &maps);
+ LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata="
+ << reinterpret_cast<const void*>(begin_) << " != expected="
+ << reinterpret_cast<const void*>(requested_base)
+ << " /proc/self/maps:\n" << maps;
+ return false;
+ }
+ end_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatlastword"));
+ if (end_ == NULL) {
+ LOG(WARNING) << "Failed to find oatlastword symbol in " << elf_filename << ": " << dlerror();
+ return false;
+ }
+ // Readjust to be non-inclusive upper bound.
+ end_ += sizeof(uint32_t);
+ Setup();
+ return true;
+}
- const byte* oat = map->Begin();
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable) {
+ elf_file_.reset(ElfFile::Open(file, writable, true));
+ if (elf_file_.get() == NULL) {
+ PLOG(WARNING) << "Failed to create ELF file for " << file->GetPath();
+ return false;
+ }
+ bool loaded = elf_file_->Load();
+ if (!loaded) {
+ LOG(WARNING) << "Failed to load ELF file " << file->GetPath();
+ return false;
+ }
+ begin_ = elf_file_->FindDynamicSymbolAddress("oatdata");
+ if (begin_ == NULL) {
+ LOG(WARNING) << "Failed to find oatdata symbol in " << file->GetPath();
+ return false;
+ }
+ if (requested_base != NULL && begin_ != requested_base) {
+ std::string maps;
+ ReadFileToString("/proc/self/maps", &maps);
+ LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata="
+ << reinterpret_cast<const void*>(begin_) << " != expected="
+ << reinterpret_cast<const void*>(requested_base)
+ << " /proc/self/maps:\n" << maps;
+ return false;
+ }
+ end_ = elf_file_->FindDynamicSymbolAddress("oatlastword");
+ if (end_ == NULL) {
+ LOG(WARNING) << "Failed to find oatlastword symbol in " << file->GetPath();
+ return false;
+ }
+ // Readjust to be non-inclusive upper bound.
+ end_ += sizeof(uint32_t);
+ Setup();
+ return true;
+}
+void OatFile::Setup() {
+ const byte* oat = Begin();
oat += sizeof(OatHeader);
- oat += oat_header.GetImageFileLocationSize();
+ oat += GetOatHeader().GetImageFileLocationSize();
- CHECK_LE(oat, map->End())
- << reinterpret_cast<void*>(map->Begin())
+ CHECK_LE(oat, End())
+ << reinterpret_cast<const void*>(Begin())
<< "+" << sizeof(OatHeader)
- << "+" << oat_header.GetImageFileLocationSize()
- << "<=" << reinterpret_cast<void*>(map->End())
- << " " << file.GetPath() << " for " << GetLocation();
- for (size_t i = 0; i < oat_header.GetDexFileCount(); i++) {
+ << "+" << GetOatHeader().GetImageFileLocationSize()
+ << "<=" << reinterpret_cast<const void*>(End())
+ << " " << GetLocation();
+ for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) {
size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
- CHECK_GT(dex_file_location_size, 0U) << file.GetPath() << " for " << GetLocation();
+ CHECK_GT(dex_file_location_size, 0U) << GetLocation();
oat += sizeof(dex_file_location_size);
- CHECK_LT(oat, map->End()) << file.GetPath() << " for " << GetLocation();
+ CHECK_LT(oat, End()) << GetLocation();
const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
oat += dex_file_location_size;
- CHECK_LT(oat, map->End()) << file.GetPath() << " for " << GetLocation();
+ CHECK_LT(oat, End()) << GetLocation();
std::string dex_file_location(dex_file_location_data, dex_file_location_size);
uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
oat += sizeof(dex_file_checksum);
- CHECK_LT(oat, map->End()) << file.GetPath() << " for " << GetLocation();
+ CHECK_LT(oat, End()) << GetLocation();
uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
- CHECK_GT(dex_file_offset, 0U) << file.GetPath() << " for " << GetLocation();
- CHECK_LT(dex_file_offset, static_cast<uint32_t>(file.GetLength()))
- << file.GetPath() << " for " << GetLocation();
+ CHECK_GT(dex_file_offset, 0U) << GetLocation();
+ CHECK_LT(dex_file_offset, Size()) << GetLocation();
oat += sizeof(dex_file_offset);
- CHECK_LT(oat, map->End()) << file.GetPath() << " for " << GetLocation();
+ CHECK_LT(oat, End()) << GetLocation();
- uint8_t* dex_file_pointer = map->Begin() + dex_file_offset;
+ const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
CHECK(DexFile::IsMagicValid(dex_file_pointer))
- << file.GetPath() << " for " << GetLocation() << " " << dex_file_pointer;
+ << GetLocation() << " " << dex_file_pointer;
CHECK(DexFile::IsVersionValid(dex_file_pointer))
- << file.GetPath() << " for " << GetLocation() << " " << dex_file_pointer;
+ << GetLocation() << " " << dex_file_pointer;
const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);
oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
- CHECK_LE(oat, map->End()) << file.GetPath() << " for " << GetLocation();
+ CHECK_LE(oat, End()) << GetLocation();
oat_dex_files_.Put(dex_file_location, new OatDexFile(this,
dex_file_location,
@@ -169,9 +233,6 @@
dex_file_pointer,
methods_offsets_pointer));
}
-
- mem_map_.reset(map.release());
- return true;
}
const OatHeader& OatFile::GetOatHeader() const {
@@ -179,13 +240,13 @@
}
const byte* OatFile::Begin() const {
- CHECK(mem_map_->Begin() != NULL);
- return mem_map_->Begin();
+ CHECK(begin_ != NULL);
+ return begin_;
}
const byte* OatFile::End() const {
- CHECK(mem_map_->End() != NULL);
- return mem_map_->End();
+ CHECK(end_ != NULL);
+ return end_;
}
const OatFile::OatDexFile* OatFile::GetOatDexFile(const std::string& dex_file_location,
@@ -211,7 +272,7 @@
OatFile::OatDexFile::OatDexFile(const OatFile* oat_file,
const std::string& dex_file_location,
uint32_t dex_file_location_checksum,
- byte* dex_file_pointer,
+ const byte* dex_file_pointer,
const uint32_t* oat_class_offsets_pointer)
: oat_file_(oat_file),
dex_file_location_(dex_file_location),
diff --git a/src/oat_file.h b/src/oat_file.h
index 9161230..ff5cd80 100644
--- a/src/oat_file.h
+++ b/src/oat_file.h
@@ -17,16 +17,20 @@
#ifndef ART_SRC_OAT_FILE_H_
#define ART_SRC_OAT_FILE_H_
+#include <string>
#include <vector>
-#include "dex_file.h"
-#include "invoke_type.h"
-#include "mem_map.h"
-#include "oat.h"
+#include "globals.h"
#include "object.h"
+#include "os.h"
namespace art {
+class ElfFile;
+class MemMap;
+class OatMethodOffsets;
+struct OatHeader;
+
class OatFile {
public:
// Returns an OatFile name based on a DexFile location
@@ -36,14 +40,17 @@
// optionally be used to request where the file should be loaded.
static OatFile* Open(const std::string& filename,
const std::string& location,
- byte* requested_base,
- bool writable = false);
+ byte* requested_base);
- // Open an oat file from an already opened File with the given location.
- static OatFile* Open(File& file,
+ // Open an oat file from an already opened File.
+ static OatFile* Open(File* file,
const std::string& location,
byte* requested_base,
- bool writable = false);
+ bool writable);
+
+ // Open an oat file backed by a std::vector with the given location.
+ static OatFile* Open(std::vector<uint8_t>& oat_contents,
+ const std::string& location);
~OatFile();
@@ -195,7 +202,7 @@
OatDexFile(const OatFile* oat_file,
const std::string& dex_file_location,
uint32_t dex_file_checksum,
- byte* dex_file_pointer,
+ const byte* dex_file_pointer,
const uint32_t* oat_class_offsets_pointer);
const OatFile* oat_file_;
@@ -217,8 +224,21 @@
}
private:
+ static void CheckLocation(const std::string& location);
+
+ static OatFile* OpenDlopen(const std::string& elf_filename,
+ const std::string& location,
+ byte* requested_base);
+
+ static OatFile* OpenElfFile(File* file,
+ const std::string& location,
+ byte* requested_base,
+ bool writable);
+
explicit OatFile(const std::string& filename);
- bool Map(File& file, byte* requested_base, bool writable);
+ bool Dlopen(const std::string& elf_filename, byte* requested_base);
+ bool ElfFileOpen(File* file, byte* requested_base, bool writable);
+ void Setup();
const byte* Begin() const;
const byte* End() const;
@@ -228,9 +248,21 @@
// The image will embed this to link its associated oat file.
const std::string location_;
- // backing memory map for oat file
+ // Pointer to OatHeader.
+ const byte* begin_;
+
+ // Pointer to end of oat region for bounds checking.
+ const byte* end_;
+
+ // Backing memory map for oat file during when opened by ElfWriter during initial compilation.
UniquePtr<MemMap> mem_map_;
+ // Backing memory map for oat file during cross compilation.
+ UniquePtr<ElfFile> elf_file_;
+
+ // dlopen handle during runtime.
+ void* dlopen_handle_;
+
typedef SafeMap<std::string, const OatDexFile*> Table;
Table oat_dex_files_;
diff --git a/src/oat_test.cc b/src/oat_test.cc
index 6f0e7b6..ec0fa7d 100644
--- a/src/oat_test.cc
+++ b/src/oat_test.cc
@@ -16,7 +16,7 @@
#include "oat_file.h"
#include "oat_writer.h"
-#include "file_output_stream.h"
+#include "vector_output_stream.h"
#include "common_test.h"
@@ -80,27 +80,28 @@
ScopedObjectAccess soa(Thread::Current());
ScratchFile tmp;
- FileOutputStream file_output_stream(tmp.GetFile());
- bool success = OatWriter::Create(file_output_stream,
- class_linker->GetBootClassPath(),
- 42U,
- 4096U,
- "lue.art",
- *compiler_.get());
- ASSERT_TRUE(success);
+ std::vector<uint8_t> oat_contents;
+ VectorOutputStream output_stream(tmp.GetFilename(), oat_contents);
+ bool success_oat = OatWriter::Create(output_stream,
+ class_linker->GetBootClassPath(),
+ 42U,
+ 4096U,
+ "lue.art",
+ *compiler_.get());
+ ASSERT_TRUE(success_oat);
+ bool success_elf = compiler_->WriteElf(oat_contents, tmp.GetFile());
+ ASSERT_TRUE(success_elf);
if (compile) { // OatWriter strips the code, regenerate to compare
compiler_->CompileAll(class_loader, class_linker->GetBootClassPath());
}
- UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
- tmp.GetFilename(),
- NULL));
+ UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL));
ASSERT_TRUE(oat_file.get() != NULL);
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
ASSERT_EQ(1U, oat_header.GetDexFileCount());
ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
- ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatBegin());
+ ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
ASSERT_EQ("lue.art", oat_header.GetImageFileLocation());
const DexFile* dex_file = java_lang_dex_file_;
diff --git a/src/oatdump.cc b/src/oatdump.cc
index cac89fd..5ee433c 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -32,6 +32,7 @@
#include "gc/space.h"
#include "image.h"
#include "indenter.h"
+#include "oat.h"
#include "object_utils.h"
#include "os.h"
#include "runtime.h"
@@ -118,7 +119,7 @@
os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
os << "IMAGE FILE LOCATION OAT BEGIN:\n";
- os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatBegin());
+ os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatDataBegin());
os << "IMAGE FILE LOCATION:\n";
const std::string image_file_location(oat_header.GetImageFileLocation());
@@ -218,7 +219,7 @@
// If the last thing in the file is code for a method, there won't be an offset for the "next"
// thing. Instead of having a special case in the upper_bound code, let's just add an entry
// for the end of the file.
- offsets_.insert(static_cast<uint32_t>(oat_file_.End() - oat_file_.Begin()));
+ offsets_.insert(static_cast<uint32_t>(oat_file_.Size()));
}
void AddOffsets(const OatFile::OatMethod& oat_method) {
@@ -696,9 +697,13 @@
os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
- os << "OAT BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n";
+ os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n";
- os << "OAT END:" << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n";
+ os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n";
+
+ os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n";
+
+ os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
{
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
diff --git a/src/runtime.cc b/src/runtime.cc
index 4cf7d97..5c73fef 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -244,6 +244,8 @@
// libpthread, which means the stacks we dump would be useless. Calling
// tgkill(2) directly avoids that.
syscall(__NR_tgkill, getpid(), GetTid(), SIGABRT);
+ // TODO: LLVM installs it's own SIGABRT handler so exit to be safe... Can we disable that?
+ exit(1);
#endif
// notreached
}
diff --git a/test/run-test b/test/run-test
index 21f4617..c404491 100755
--- a/test/run-test
+++ b/test/run-test
@@ -237,8 +237,11 @@
if [ "$build_exit" = '0' ]; then
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" 2>&1
- echo "run exit status: $?" 1>&2
- good="yes"
+ run_exit="$?"
+ echo "run exit status: $run_exit" 1>&2
+ if [ "$run_exit" = "0" ]; then
+ good="yes"
+ fi
fi
elif [ "$update_mode" = "yes" ]; then
"./${build}" >"$build_output" 2>&1