diff options
author | Brian Carlstrom <bdc@google.com> | 2013-07-11 16:00:56 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2013-07-11 21:16:04 -0700 |
commit | 51c2467e8771b56e25ae4f17f66522f979f57a7e (patch) | |
tree | 16a66abbb45ea3fcc8c513c8511f34a46cd454c4 /src/compiler | |
parent | 27c590edf6c24107b6473e84e526c432bd081f6e (diff) | |
download | android_art-51c2467e8771b56e25ae4f17f66522f979f57a7e.tar.gz android_art-51c2467e8771b56e25ae4f17f66522f979f57a7e.tar.bz2 android_art-51c2467e8771b56e25ae4f17f66522f979f57a7e.zip |
libart-compiler cleanup
- Move compile-time code to src/compiler and libart-compiler
OatWriter, ImageWriter, ElfWriter, ElfFixup, ElfStripper, stub generation
- Move ClassReference and MethodReference to remove MethodVerifier dependency on CompilerDriver
- Move runtime_support_llvm.cc out of src/compiler and next to runtime_support.cc
- Change dex2oat and gtests to directly depend on libart-compiler
- Move non-common definitions from Android.common.mk to more specific makefiles
- Add LOCAL_ADDITIONAL_DEPENDENCIES on appropriate makefiles
Change-Id: I897027e69945914128f21f317a92caf9255bc600
Diffstat (limited to 'src/compiler')
34 files changed, 4059 insertions, 1199 deletions
diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc index afb29f4163..ce512ec382 100644 --- a/src/compiler/dex/dex_to_dex_compiler.cc +++ b/src/compiler/dex/dex_to_dex_compiler.cc @@ -206,7 +206,7 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, return; } uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - CompilerDriver::MethodReference target_method(&GetDexFile(), method_idx); + MethodReference target_method(&GetDexFile(), method_idx); InvokeType invoke_type = kVirtual; InvokeType original_invoke_type = invoke_type; int vtable_idx; diff --git a/src/compiler/dex/mir_dataflow.cc b/src/compiler/dex/mir_dataflow.cc index 3b2c1a6c5e..381afb122e 100644 --- a/src/compiler/dex/mir_dataflow.cc +++ b/src/compiler/dex/mir_dataflow.cc @@ -1225,7 +1225,7 @@ bool MIRGraph::InvokeUsesMethodStar(MIR* mir) return false; } DexCompilationUnit m_unit(cu_); - CompilerDriver::MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB); + MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB); int vtable_idx; uintptr_t direct_code; uintptr_t direct_method; diff --git a/src/compiler/dex/quick/codegen_util.cc b/src/compiler/dex/quick/codegen_util.cc index ac2828c276..766cdce235 100644 --- a/src/compiler/dex/quick/codegen_util.cc +++ b/src/compiler/dex/quick/codegen_util.cc @@ -632,7 +632,7 @@ void Mir2Lir::CreateNativeGcMap() { max_native_offset = native_offset; } } - CompilerDriver::MethodReference method_ref(cu_->dex_file, cu_->method_idx); + MethodReference method_ref(cu_->dex_file, cu_->method_idx); const std::vector<uint8_t>* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref); verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[4], gc_map_raw->size() - 4); // Compute native offset to references size. diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc index 7aa71cfd10..2980acbd06 100644 --- a/src/compiler/dex/quick/gen_common.cc +++ b/src/compiler/dex/quick/gen_common.cc @@ -1077,7 +1077,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ // Note: currently type_known_final is unused, as optimizing will only improve the performance // of the exception throw path. DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit(); - const CompilerDriver::MethodReference mr(cu->GetDexFile(), cu->GetDexMethodIndex()); + const MethodReference mr(cu->GetDexFile(), cu->GetDexMethodIndex()); if (!needs_access_check && cu_->compiler_driver->IsSafeCast(mr, insn_idx)) { // Verifier type analysis proved this check cast would never cause an exception. return; diff --git a/src/compiler/dex/quick/gen_invoke.cc b/src/compiler/dex/quick/gen_invoke.cc index 4b12bb407a..3bc73401cf 100644 --- a/src/compiler/dex/quick/gen_invoke.cc +++ b/src/compiler/dex/quick/gen_invoke.cc @@ -313,7 +313,7 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) * emit the next instruction in static & direct invoke sequences. */ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, - int state, const CompilerDriver::MethodReference& target_method, + int state, const MethodReference& target_method, uint32_t unused, uintptr_t direct_code, uintptr_t direct_method, InvokeType type) @@ -418,7 +418,7 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, * kArg1 here rather than the standard LoadArgRegs. */ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, - int state, const CompilerDriver::MethodReference& target_method, + int state, const MethodReference& target_method, uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) { @@ -467,7 +467,7 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, * which will locate the target and continue on via a tail call. */ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t unused, uintptr_t unused2, uintptr_t direct_method, InvokeType unused4) { @@ -535,7 +535,7 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, } static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, int trampoline, - int state, const CompilerDriver::MethodReference& target_method, + int state, const MethodReference& target_method, uint32_t method_idx) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); @@ -558,7 +558,7 @@ static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, int trampoline, static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) @@ -568,7 +568,7 @@ static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, } static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) { @@ -577,7 +577,7 @@ static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, } static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) { @@ -586,7 +586,7 @@ static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, } static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t method_idx, uintptr_t unused, uintptr_t unused2, InvokeType unused3) { @@ -596,7 +596,7 @@ static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, CallInfo* info, int state, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t unused, uintptr_t unused2, uintptr_t unused3, InvokeType unused4) @@ -607,7 +607,7 @@ static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, NextCallInsn next_call_insn, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { @@ -647,7 +647,7 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, */ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { @@ -747,7 +747,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, */ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { @@ -1373,7 +1373,7 @@ void Mir2Lir::GenInvoke(CallInfo* info) LockCallTemps(); DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit(); - CompilerDriver::MethodReference target_method(cUnit->GetDexFile(), info->index); + MethodReference target_method(cUnit->GetDexFile(), info->index); int vtable_idx; uintptr_t direct_code; uintptr_t direct_method; diff --git a/src/compiler/dex/quick/mir_to_lir.h b/src/compiler/dex/quick/mir_to_lir.h index 9eb4524d0d..93098dbfd1 100644 --- a/src/compiler/dex/quick/mir_to_lir.h +++ b/src/compiler/dex/quick/mir_to_lir.h @@ -100,7 +100,7 @@ class MIRGraph; class Mir2Lir; typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t method_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type); @@ -467,13 +467,13 @@ class Mir2Lir : public Backend { void FlushIns(RegLocation* ArgLocs, RegLocation rl_method); int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel, NextCallInsn next_call_insn, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); @@ -495,7 +495,7 @@ class Mir2Lir : public Backend { bool GenIntrinsic(CallInfo* info); int LoadArgRegs(CallInfo* info, int call_state, NextCallInsn next_call_insn, - const CompilerDriver::MethodReference& target_method, + const MethodReference& target_method, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); diff --git a/src/compiler/dex/write_elf.cc b/src/compiler/dex/write_elf.cc deleted file mode 100644 index d9e45a9c19..0000000000 --- a/src/compiler/dex/write_elf.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "elf_fixup.h" -#include "elf_stripper.h" -#include "os.h" - -#if defined(ART_USE_PORTABLE_COMPILER) -#include "elf_writer_mclinker.h" -#else -#include "elf_writer_quick.h" -#endif - -namespace art { -class CompilerDriver; -class DexFile; -} // namespace art - -extern "C" bool WriteElf(art::CompilerDriver& driver, - const std::string& android_root, - bool is_host, - const std::vector<const art::DexFile*>& dex_files, - std::vector<uint8_t>& oat_contents, - art::File* file) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(ART_USE_PORTABLE_COMPILER) - return art::ElfWriterMclinker::Create(file, oat_contents, dex_files, android_root, is_host, driver); -#else - return art::ElfWriterQuick::Create(file, oat_contents, dex_files, android_root, is_host, driver); -#endif -} -extern "C" bool FixupElf(art::File* file, uintptr_t oat_data_begin) { - return art::ElfFixup::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); -} -extern "C" bool StripElf(art::File* file) { - return art::ElfStripper::Strip(file); -} diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index c8ccbe9dbb..c876252d45 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -18,7 +18,6 @@ #include <vector> -#include <dlfcn.h> #include <unistd.h> #include "base/stl_util.h" @@ -48,8 +47,10 @@ #include "thread_pool.h" #include "verifier/method_verifier.h" -#if defined(__APPLE__) -#include <mach-o/dyld.h> +#if defined(ART_USE_PORTABLE_COMPILER) +#include "compiler/elf_writer_mclinker.h" +#else +#include "compiler/elf_writer_quick.h" #endif namespace art { @@ -281,48 +282,57 @@ class AOTCompilationStats { DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats); }; -static std::string MakeCompilerSoName(CompilerBackend compiler_backend) { - - // Bad things happen if we pull in the libartd-compiler to a libart dex2oat or vice versa, - // because we end up with both libart and libartd in the same address space! - const char* suffix = (kIsDebugBuild ? "d" : ""); - - // Work out the filename for the compiler library. - std::string library_name(StringPrintf("art%s-compiler", suffix)); - std::string filename(StringPrintf(OS_SHARED_LIB_FORMAT_STR, library_name.c_str())); - -#if defined(__APPLE__) - // On Linux, dex2oat will have been built with an RPATH of $ORIGIN/../lib, so dlopen(3) will find - // the .so by itself. On Mac OS, there isn't really an equivalent, so we have to manually do the - // same work. - uint32_t executable_path_length = 0; - _NSGetExecutablePath(NULL, &executable_path_length); - std::string path(executable_path_length, static_cast<char>(0)); - CHECK_EQ(_NSGetExecutablePath(&path[0], &executable_path_length), 0); - - // Strip the "/dex2oat". - size_t last_slash = path.find_last_of('/'); - CHECK_NE(last_slash, std::string::npos) << path; - path.resize(last_slash); - - // Strip the "/bin". - last_slash = path.find_last_of('/'); - path.resize(last_slash); - - filename = path + "/lib/" + filename; -#endif - return filename; -} - -template<typename Fn> -static Fn FindFunction(const std::string& compiler_so_name, void* library, const char* name) { - Fn fn = reinterpret_cast<Fn>(dlsym(library, name)); - if (fn == NULL) { - LOG(FATAL) << "Couldn't find \"" << name << "\" in compiler library " << compiler_so_name << ": " << dlerror(); - } - VLOG(compiler) << "Found \"" << name << "\" at " << reinterpret_cast<void*>(fn); - return fn; -} +extern "C" void ArtInitCompilerContext(art::CompilerDriver& driver); +extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler); + +extern "C" void ArtUnInitCompilerContext(art::CompilerDriver& driver); +extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler); + +extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, + const art::DexFile::CodeItem* code_item, + uint32_t access_flags, + art::InvokeType invoke_type, + uint32_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const art::DexFile& dex_file); +extern "C" art::CompiledMethod* ArtQuickCompileMethod(art::CompilerDriver& compiler, + const art::DexFile::CodeItem* code_item, + uint32_t access_flags, + art::InvokeType invoke_type, + uint32_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const art::DexFile& dex_file); + +extern "C" art::CompiledMethod* ArtCompileDEX(art::CompilerDriver& compiler, + const art::DexFile::CodeItem* code_item, + uint32_t access_flags, + art::InvokeType invoke_type, + uint32_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const art::DexFile& dex_file); + +extern "C" art::CompiledMethod* SeaIrCompileMethod(art::CompilerDriver& compiler, + const art::DexFile::CodeItem* code_item, + uint32_t access_flags, + art::InvokeType invoke_type, + uint32_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const art::DexFile& dex_file); + +extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver& driver, + uint32_t access_flags, uint32_t method_idx, + const art::DexFile& dex_file); + +extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver& compiler, + uint32_t access_flags, uint32_t method_idx, + const art::DexFile& dex_file); + +extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, + std::string const& filename); CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, bool image, DescriptorSet* image_classes, @@ -349,13 +359,6 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiler_get_method_code_addr_(NULL), support_boot_image_fixup_(true) { - std::string compiler_so_name(MakeCompilerSoName(compiler_backend_)); - compiler_library_ = dlopen(compiler_so_name.c_str(), RTLD_LAZY); - if (compiler_library_ == NULL) { - LOG(FATAL) << "Couldn't find compiler library " << compiler_so_name << ": " << dlerror(); - } - VLOG(compiler) << "dlopen(\"" << compiler_so_name << "\", RTLD_LAZY) returned " << compiler_library_; - CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key"); // TODO: more work needed to combine initializations and allow per-method backend selection @@ -363,28 +366,28 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet InitCompilerContextFn init_compiler_context; if (compiler_backend_ == kPortable){ // Initialize compiler_context_ - init_compiler_context = FindFunction<void (*)(CompilerDriver&)>(compiler_so_name, - compiler_library_, "ArtInitCompilerContext"); - compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtCompileMethod"); + init_compiler_context = reinterpret_cast<void (*)(CompilerDriver&)>(ArtInitCompilerContext); + compiler_ = reinterpret_cast<CompilerFn>(ArtCompileMethod); } else { - init_compiler_context = FindFunction<void (*)(CompilerDriver&)>(compiler_so_name, - compiler_library_, "ArtInitQuickCompilerContext"); - compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtQuickCompileMethod"); + init_compiler_context = reinterpret_cast<void (*)(CompilerDriver&)>(ArtInitQuickCompilerContext); + compiler_ = reinterpret_cast<CompilerFn>(ArtQuickCompileMethod); } - dex_to_dex_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtCompileDEX"); + dex_to_dex_compiler_ = reinterpret_cast<CompilerFn>(ArtCompileDEX); +#ifdef ART_SEA_IR_MODE sea_ir_compiler_ = NULL; if (Runtime::Current()->IsSeaIRMode()) { - sea_ir_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "SeaIrCompileMethod"); + sea_ir_compiler_ = reinterpret_cast<CompilerFn>(SeaIrCompileMethod); } +#endif init_compiler_context(*this); if (compiler_backend_ == kPortable) { - jni_compiler_ = FindFunction<JniCompilerFn>(compiler_so_name, compiler_library_, "ArtLLVMJniCompileMethod"); + jni_compiler_ = reinterpret_cast<JniCompilerFn>(ArtLLVMJniCompileMethod); } else { - jni_compiler_ = FindFunction<JniCompilerFn>(compiler_so_name, compiler_library_, "ArtQuickJniCompileMethod"); + jni_compiler_ = reinterpret_cast<JniCompilerFn>(ArtQuickJniCompileMethod); } CHECK(!Runtime::Current()->IsStarted()); @@ -413,39 +416,15 @@ CompilerDriver::~CompilerDriver() { } CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key"); typedef void (*UninitCompilerContextFn)(CompilerDriver&); - std::string compiler_so_name(MakeCompilerSoName(compiler_backend_)); UninitCompilerContextFn uninit_compiler_context; // Uninitialize compiler_context_ // TODO: rework to combine initialization/uninitialization if (compiler_backend_ == kPortable) { - uninit_compiler_context = FindFunction<void (*)(CompilerDriver&)>(compiler_so_name, - compiler_library_, "ArtUnInitCompilerContext"); + uninit_compiler_context = reinterpret_cast<void (*)(CompilerDriver&)>(ArtUnInitCompilerContext); } else { - uninit_compiler_context = FindFunction<void (*)(CompilerDriver&)>(compiler_so_name, - compiler_library_, "ArtUnInitQuickCompilerContext"); + uninit_compiler_context = reinterpret_cast<void (*)(CompilerDriver&)>(ArtUnInitQuickCompilerContext); } uninit_compiler_context(*this); -#if 0 - if (compiler_library_ != NULL) { - VLOG(compiler) << "dlclose(" << compiler_library_ << ")"; - /* - * FIXME: Temporary workaround - * Apparently, llvm is adding dctors to atexit, but if we unload - * the library here the code will no longer be around at exit time - * and we die a flaming death in __cxa_finalize(). Apparently, some - * dlclose() implementations will scan the atexit list on unload and - * handle any associated with the soon-to-be-unloaded library. - * However, this is not required by POSIX and we don't do it. - * See: http://b/issue?id=4998315 - * What's the right thing to do here? - * - * This has now been completely disabled because mclinker was - * closing stdout on exit, which was affecting both quick and - * portable. - */ - dlclose(compiler_library_); - } -#endif } CompilerTls* CompilerDriver::GetTls() { @@ -1217,9 +1196,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui if (kEnableVerifierBasedSharpening && (invoke_type == kVirtual || invoke_type == kInterface)) { // Did the verifier record a more precise invoke target based on its type information? - const CompilerDriver::MethodReference caller_method(mUnit->GetDexFile(), - mUnit->GetDexMethodIndex()); - const CompilerDriver::MethodReference* devirt_map_target = + const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex()); + const MethodReference* devirt_map_target = verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc); if (devirt_map_target != NULL) { mirror::DexCache* target_dex_cache = @@ -2138,7 +2116,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } // Record the final class status if necessary. mirror::Class::Status status = klass->GetStatus(); - CompilerDriver::ClassReference ref(manager->GetDexFile(), class_def_index); + ClassReference ref(manager->GetDexFile(), class_def_index); CompiledClass* compiled_class = manager->GetCompiler()->GetCompiledClass(ref); if (compiled_class == NULL) { compiled_class = new CompiledClass(status); @@ -2287,21 +2265,16 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t dont_compile = false; } if (!dont_compile) { - bool use_sea = false; - - if (Runtime::Current()->IsSeaIRMode()) { - use_sea = true; - } + CompilerFn compiler = compiler_; +#ifdef ART_SEA_IR_MODE + bool use_sea = Runtime::Current()->IsSeaIRMode(); + use_sea &&= (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci")); if (use_sea) { - use_sea = (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci")); - } - if (!use_sea) { - compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, - method_idx, class_loader, dex_file); - } else { - compiled_method = (*sea_ir_compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, - method_idx, class_loader, dex_file); + compiler = sea_ir_compiler_; } +#endif + compiled_method = (*compiler)(*this, code_item, access_flags, invoke_type, class_def_idx, + method_idx, class_loader, dex_file); CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); } else if (allow_dex_to_dex_compilation) { // TODO: add a mode to disable DEX-to-DEX compilation ? @@ -2365,8 +2338,7 @@ void CompilerDriver::SetBitcodeFileName(std::string const& filename) { typedef void (*SetBitcodeFileNameFn)(CompilerDriver&, std::string const&); SetBitcodeFileNameFn set_bitcode_file_name = - FindFunction<SetBitcodeFileNameFn>(MakeCompilerSoName(compiler_backend_), compiler_library_, - "compilerLLVMSetBitcodeFileName"); + reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName); set_bitcode_file_name(*this, filename); } @@ -2386,45 +2358,16 @@ bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex bool CompilerDriver::WriteElf(const std::string& android_root, bool is_host, - const std::vector<const DexFile*>& dex_files, + const std::vector<const art::DexFile*>& dex_files, std::vector<uint8_t>& oat_contents, - File* file) { - typedef bool (*WriteElfFn)(CompilerDriver&, - const std::string& android_root, - bool is_host, - const std::vector<const DexFile*>& dex_files, - std::vector<uint8_t>&, - File*); - WriteElfFn WriteElf = - FindFunction<WriteElfFn>(MakeCompilerSoName(compiler_backend_), compiler_library_, "WriteElf"); - Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); - return WriteElf(*this, android_root, is_host, dex_files, oat_contents, file); -} - -bool CompilerDriver::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 CompilerDriver::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); -} - -bool CompilerDriver::StripElf(File* file) const { - typedef bool (*StripElfFn)(File*); - StripElfFn StripElf = - FindFunction<StripElfFn>(MakeCompilerSoName(compiler_backend_), compiler_library_, "StripElf"); - return StripElf(file); + art::File* file) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +#if defined(ART_USE_PORTABLE_COMPILER) + return art::ElfWriterMclinker::Create(file, oat_contents, dex_files, android_root, is_host, *this); +#else + return art::ElfWriterQuick::Create(file, oat_contents, dex_files, android_root, is_host, *this); +#endif } - void CompilerDriver::InstructionSetToLLVMTarget(InstructionSet instruction_set, std::string& target_triple, std::string& target_cpu, diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index b37b74b042..d37f494ef1 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -22,11 +22,13 @@ #include <vector> #include "base/mutex.h" +#include "class_reference.h" #include "compiled_class.h" #include "compiled_method.h" #include "dex_file.h" #include "instruction_set.h" #include "invoke_type.h" +#include "method_reference.h" #include "oat_file.h" #include "runtime.h" #include "safe_map.h" @@ -114,30 +116,9 @@ class CompilerDriver { const std::vector<uint8_t>* CreateInterpreterToQuickEntry() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // A class is uniquely located by its DexFile and the class_defs_ table index into that DexFile - typedef std::pair<const DexFile*, uint32_t> ClassReference; - CompiledClass* GetCompiledClass(ClassReference ref) const LOCKS_EXCLUDED(compiled_classes_lock_); - // A method is uniquely located by its DexFile and the method_ids_ table index into that DexFile - struct MethodReference { - MethodReference(const DexFile* file, uint32_t index) : dex_file(file), dex_method_index(index) { - } - const DexFile* dex_file; - uint32_t dex_method_index; - }; - - struct MethodReferenceComparator { - bool operator()(MethodReference mr1, MethodReference mr2) const { - if (mr1.dex_file == mr2.dex_file) { - return mr1.dex_method_index < mr2.dex_method_index; - } else { - return mr1.dex_file < mr2.dex_file; - } - } - }; - CompiledMethod* GetCompiledMethod(MethodReference ref) const LOCKS_EXCLUDED(compiled_methods_lock_); @@ -212,15 +193,11 @@ class CompilerDriver { } - // TODO: remove these Elf wrappers when libart links against LLVM (when separate compiler library is gone) bool WriteElf(const std::string& android_root, bool is_host, const std::vector<const DexFile*>& dex_files, 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; - bool StripElf(File* file) const; // TODO: move to a common home for llvm helpers once quick/portable are merged static void InstructionSetToLLVMTarget(InstructionSet instruction_set, @@ -404,7 +381,9 @@ class CompilerDriver { uint32_t class_dex_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file); CompilerFn compiler_; +#ifdef ART_SEA_IR_MODE CompilerFn sea_ir_compiler_; +#endif CompilerFn dex_to_dex_compiler_; @@ -429,16 +408,6 @@ class CompilerDriver { DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; -inline bool operator<(const CompilerDriver::ClassReference& lhs, const CompilerDriver::ClassReference& rhs) { - if (lhs.second < rhs.second) { - return true; - } else if (lhs.second > rhs.second) { - return false; - } else { - return (lhs.first < rhs.first); - } -} - } // namespace art #endif // ART_SRC_COMPILER_DRIVER_COMPILER_DRIVER_H_ diff --git a/src/compiler/elf_fixup.cc b/src/compiler/elf_fixup.cc new file mode 100644 index 0000000000..127bc85d2c --- /dev/null +++ b/src/compiler/elf_fixup.cc @@ -0,0 +1,279 @@ +/* + * 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_fixup.h" + +#include "base/logging.h" +#include "base/stringprintf.h" +#include "elf_file.h" +#include "elf_writer.h" +#include "UniquePtr.h" + +namespace art { + +static const bool DEBUG_FIXUP = false; + +bool ElfFixup::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 = ElfWriter::GetOatDataAddress(elf_file.get()); + ::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; + } + if (!FixupRelocations(*elf_file.get(), base_address)) { + LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath(); + return false; + } + return true; +} + +// MIPS seems to break the rules d_val vs d_ptr even though their values are between DT_LOPROC and DT_HIPROC +#define DT_MIPS_RLD_VERSION 0x70000001 // d_val +#define DT_MIPS_TIME_STAMP 0x70000002 // d_val +#define DT_MIPS_ICHECKSUM 0x70000003 // d_val +#define DT_MIPS_IVERSION 0x70000004 // d_val +#define DT_MIPS_FLAGS 0x70000005 // d_val +#define DT_MIPS_BASE_ADDRESS 0x70000006 // d_ptr +#define DT_MIPS_CONFLICT 0x70000008 // d_ptr +#define DT_MIPS_LIBLIST 0x70000009 // d_ptr +#define DT_MIPS_LOCAL_GOTNO 0x7000000A // d_val +#define DT_MIPS_CONFLICTNO 0x7000000B // d_val +#define DT_MIPS_LIBLISTNO 0x70000010 // d_val +#define DT_MIPS_SYMTABNO 0x70000011 // d_val +#define DT_MIPS_UNREFEXTNO 0x70000012 // d_val +#define DT_MIPS_GOTSYM 0x70000013 // d_val +#define DT_MIPS_HIPAGENO 0x70000014 // d_val +#define DT_MIPS_RLD_MAP 0x70000016 // d_ptr + +bool ElfFixup::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); + ::llvm::ELF::Elf32_Word d_tag = elf_dyn.d_tag; + bool elf_dyn_needs_fixup = false; + switch (d_tag) { + // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr + 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; + } + // d_val or ignored values + case ::llvm::ELF::DT_NULL: + case ::llvm::ELF::DT_NEEDED: + case ::llvm::ELF::DT_PLTRELSZ: + case ::llvm::ELF::DT_RELASZ: + case ::llvm::ELF::DT_RELAENT: + case ::llvm::ELF::DT_STRSZ: + case ::llvm::ELF::DT_SYMENT: + case ::llvm::ELF::DT_SONAME: + case ::llvm::ELF::DT_RPATH: + case ::llvm::ELF::DT_SYMBOLIC: + case ::llvm::ELF::DT_RELSZ: + case ::llvm::ELF::DT_RELENT: + case ::llvm::ELF::DT_PLTREL: + case ::llvm::ELF::DT_TEXTREL: + case ::llvm::ELF::DT_BIND_NOW: + case ::llvm::ELF::DT_INIT_ARRAYSZ: + case ::llvm::ELF::DT_FINI_ARRAYSZ: + case ::llvm::ELF::DT_RUNPATH: + case ::llvm::ELF::DT_FLAGS: { + break; + } + // boundary values that should not be used + case ::llvm::ELF::DT_ENCODING: + case ::llvm::ELF::DT_LOOS: + case ::llvm::ELF::DT_HIOS: + case ::llvm::ELF::DT_LOPROC: + case ::llvm::ELF::DT_HIPROC: { + LOG(FATAL) << "Illegal d_tag value 0x" << std::hex << d_tag; + break; + } + default: { + // case 2: "regular" DT_* ranges where even d_tag values imply an address in d_ptr + if ((::llvm::ELF::DT_ENCODING < d_tag && d_tag < ::llvm::ELF::DT_LOOS) + || (::llvm::ELF::DT_LOOS < d_tag && d_tag < ::llvm::ELF::DT_HIOS) + || (::llvm::ELF::DT_LOPROC < d_tag && d_tag < ::llvm::ELF::DT_HIPROC)) { + // Special case for MIPS which breaks the regular rules between DT_LOPROC and DT_HIPROC + if (elf_file.GetHeader().e_machine == ::llvm::ELF::EM_MIPS) { + switch (d_tag) { + case DT_MIPS_RLD_VERSION: + case DT_MIPS_TIME_STAMP: + case DT_MIPS_ICHECKSUM: + case DT_MIPS_IVERSION: + case DT_MIPS_FLAGS: + case DT_MIPS_LOCAL_GOTNO: + case DT_MIPS_CONFLICTNO: + case DT_MIPS_LIBLISTNO: + case DT_MIPS_SYMTABNO: + case DT_MIPS_UNREFEXTNO: + case DT_MIPS_GOTSYM: + case DT_MIPS_HIPAGENO: { + break; + } + case DT_MIPS_BASE_ADDRESS: + case DT_MIPS_CONFLICT: + case DT_MIPS_LIBLIST: + case DT_MIPS_RLD_MAP: { + elf_dyn_needs_fixup = true; + break; + } + default: { + LOG(FATAL) << "Unknown MIPS d_tag value 0x" << std::hex << d_tag; + break; + } + } + } else if ((elf_dyn.d_tag % 2) == 0) { + elf_dyn_needs_fixup = true; + } + } else { + LOG(FATAL) << "Unknown d_tag value 0x" << std::hex << d_tag; + } + break; + } + } + if (elf_dyn_needs_fixup) { + uint32_t d_ptr = elf_dyn.d_un.d_ptr; + if (DEBUG_FIXUP) { + LOG(INFO) << StringPrintf("In %s moving Elf32_Dyn[%d] from 0x%08x to 0x%08x", + elf_file.GetFile().GetPath().c_str(), i, + d_ptr, d_ptr + base_address); + } + d_ptr += base_address; + elf_dyn.d_un.d_ptr = d_ptr; + } + } + return true; +} + +bool ElfFixup::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; + } + if (DEBUG_FIXUP) { + LOG(INFO) << StringPrintf("In %s moving Elf32_Shdr[%d] from 0x%08x to 0x%08x", + elf_file.GetFile().GetPath().c_str(), i, + sh.sh_addr, sh.sh_addr + base_address); + } + sh.sh_addr += base_address; + } + return true; +} + +bool ElfFixup::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)))) + << elf_file.GetFile().GetPath() << " i=" << i; + if (DEBUG_FIXUP) { + LOG(INFO) << StringPrintf("In %s moving Elf32_Phdr[%d] from 0x%08x to 0x%08x", + elf_file.GetFile().GetPath().c_str(), i, + ph.p_vaddr, ph.p_vaddr + base_address); + } + 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)))) + << elf_file.GetFile().GetPath() << " i=" << i; + } + return true; +} + +bool ElfFixup::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); + if (symbol_section == NULL) { + // file is missing optional .symtab + CHECK(!dynamic) << elf_file.GetFile().GetPath(); + return true; + } + 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) { + if (DEBUG_FIXUP) { + LOG(INFO) << StringPrintf("In %s moving Elf32_Sym[%d] from 0x%08x to 0x%08x", + elf_file.GetFile().GetPath().c_str(), i, + symbol.st_value, symbol.st_value + base_address); + } + symbol.st_value += base_address; + } + } + return true; +} + +bool ElfFixup::FixupRelocations(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); + if (sh.sh_type == llvm::ELF::SHT_REL) { + for (uint32_t i = 0; i < elf_file.GetRelNum(sh); i++) { + llvm::ELF::Elf32_Rel& rel = elf_file.GetRel(sh, i); + if (DEBUG_FIXUP) { + LOG(INFO) << StringPrintf("In %s moving Elf32_Rel[%d] from 0x%08x to 0x%08x", + elf_file.GetFile().GetPath().c_str(), i, + rel.r_offset, rel.r_offset + base_address); + } + rel.r_offset += base_address; + } + } else if (sh.sh_type == llvm::ELF::SHT_RELA) { + for (uint32_t i = 0; i < elf_file.GetRelaNum(sh); i++) { + llvm::ELF::Elf32_Rela& rela = elf_file.GetRela(sh, i); + if (DEBUG_FIXUP) { + LOG(INFO) << StringPrintf("In %s moving Elf32_Rela[%d] from 0x%08x to 0x%08x", + elf_file.GetFile().GetPath().c_str(), i, + rela.r_offset, rela.r_offset + base_address); + } + rela.r_offset += base_address; + } + } + } + return true; +} + +} // namespace art diff --git a/src/compiler/elf_fixup.h b/src/compiler/elf_fixup.h new file mode 100644 index 0000000000..79c45c1874 --- /dev/null +++ b/src/compiler/elf_fixup.h @@ -0,0 +1,56 @@ +/* + * 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_FIXUP_H_ +#define ART_SRC_ELF_FIXUP_H_ + +#include <stdint.h> + +#include "base/macros.h" +#include "os.h" + +namespace art { + +class ElfFile; + +class ElfFixup { + public: + // 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); + + private: + // 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); + + // Fixup dynamic relocations + static bool FixupRelocations(ElfFile& elf_file, uintptr_t base_address); + + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfFixup); +}; + +} // namespace art + +#endif // ART_SRC_ELF_FIXUP_H_ diff --git a/src/compiler/elf_stripper.cc b/src/compiler/elf_stripper.cc new file mode 100644 index 0000000000..7fc662ca1d --- /dev/null +++ b/src/compiler/elf_stripper.cc @@ -0,0 +1,129 @@ +/* + * 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_stripper.h" + +#include <vector> + +#include <llvm/Support/ELF.h> + +#include "UniquePtr.h" +#include "base/logging.h" +#include "elf_file.h" +#include "utils.h" + +namespace art { + +bool ElfStripper::Strip(File* file) { + UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false)); + CHECK(elf_file.get() != NULL); + + // ELF files produced by MCLinker look roughly like this + // + // +------------+ + // | Elf32_Ehdr | contains number of Elf32_Shdr and offset to first + // +------------+ + // | Elf32_Phdr | program headers + // | Elf32_Phdr | + // | ... | + // | Elf32_Phdr | + // +------------+ + // | section | mixture of needed and unneeded sections + // +------------+ + // | section | + // +------------+ + // | ... | + // +------------+ + // | section | + // +------------+ + // | Elf32_Shdr | section headers + // | Elf32_Shdr | + // | ... | contains offset to section start + // | Elf32_Shdr | + // +------------+ + // + // To strip: + // - leave the Elf32_Ehdr and Elf32_Phdr values in place. + // - walk the sections making a new set of Elf32_Shdr section headers for what we want to keep + // - move the sections are keeping up to fill in gaps of sections we want to strip + // - write new Elf32_Shdr section headers to end of file, updating Elf32_Ehdr + // - truncate rest of file + // + + std::vector<llvm::ELF::Elf32_Shdr> section_headers; + std::vector<llvm::ELF::Elf32_Word> section_headers_original_indexes; + section_headers.reserve(elf_file->GetSectionHeaderNum()); + + + llvm::ELF::Elf32_Shdr& string_section = elf_file->GetSectionNameStringSection(); + for (llvm::ELF::Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) { + llvm::ELF::Elf32_Shdr& sh = elf_file->GetSectionHeader(i); + const char* name = elf_file->GetString(string_section, sh.sh_name); + if (name == NULL) { + CHECK_EQ(0U, i); + section_headers.push_back(sh); + section_headers_original_indexes.push_back(0); + continue; + } + if (StartsWith(name, ".debug") + || (strcmp(name, ".strtab") == 0) + || (strcmp(name, ".symtab") == 0)) { + continue; + } + section_headers.push_back(sh); + section_headers_original_indexes.push_back(i); + } + CHECK_NE(0U, section_headers.size()); + CHECK_EQ(section_headers.size(), section_headers_original_indexes.size()); + + // section 0 is the NULL section, sections start at offset of first section + llvm::ELF::Elf32_Off offset = elf_file->GetSectionHeader(1).sh_offset; + for (size_t i = 1; i < section_headers.size(); i++) { + llvm::ELF::Elf32_Shdr& new_sh = section_headers[i]; + llvm::ELF::Elf32_Shdr& old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]); + CHECK_EQ(new_sh.sh_name, old_sh.sh_name); + if (old_sh.sh_addralign > 1) { + offset = RoundUp(offset, old_sh.sh_addralign); + } + if (old_sh.sh_offset == offset) { + // already in place + offset += old_sh.sh_size; + continue; + } + // shift section earlier + memmove(elf_file->Begin() + offset, + elf_file->Begin() + old_sh.sh_offset, + old_sh.sh_size); + new_sh.sh_offset = offset; + offset += old_sh.sh_size; + } + + llvm::ELF::Elf32_Off shoff = offset; + size_t section_headers_size_in_bytes = section_headers.size() * sizeof(llvm::ELF::Elf32_Shdr); + memcpy(elf_file->Begin() + offset, §ion_headers[0], section_headers_size_in_bytes); + offset += section_headers_size_in_bytes; + + elf_file->GetHeader().e_shnum = section_headers.size(); + elf_file->GetHeader().e_shoff = shoff; + int result = ftruncate(file->Fd(), offset); + if (result != 0) { + PLOG(ERROR) << "Failed to truncate while stripping ELF file: " << file->GetPath(); + return false; + } + return true; +} + +} // namespace art diff --git a/src/compiler/llvm/runtime_support_llvm.h b/src/compiler/elf_stripper.h index af99842089..b202e6e1f0 100644 --- a/src/compiler/llvm/runtime_support_llvm.h +++ b/src/compiler/elf_stripper.h @@ -14,17 +14,24 @@ * limitations under the License. */ -#ifndef ART_SRC_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_H_ -#define ART_SRC_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_H_ +#ifndef ART_SRC_ELF_STRIPPER_H_ +#define ART_SRC_ELF_STRIPPER_H_ -extern "C" { +#include "base/macros.h" +#include "os.h" -//---------------------------------------------------------------------------- -// Runtime Support Function Lookup Callback -//---------------------------------------------------------------------------- +namespace art { -void* art_portable_find_runtime_support_func(void* context, const char* name); +class ElfStripper { + public: + // Strip an ELF file of unneeded debugging information. + // Returns true on success, false on failure. + static bool Strip(File* file); -} // extern "C" + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper); +}; -#endif // ART_SRC_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_H_ +} // namespace art + +#endif // ART_SRC_ELF_STRIPPER_H_ diff --git a/src/compiler/elf_writer.cc b/src/compiler/elf_writer.cc new file mode 100644 index 0000000000..7592eb5edb --- /dev/null +++ b/src/compiler/elf_writer.cc @@ -0,0 +1,60 @@ +/* + * 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 "class_linker.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler/llvm/utils_llvm.h" +#include "dex_file-inl.h" +#include "dex_method_iterator.h" +#include "elf_file.h" +#include "invoke_type.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "oat.h" +#include "oat_file.h" +#include "scoped_thread_state_change.h" + +namespace art { + +ElfWriter::ElfWriter(const CompilerDriver& driver, File* elf_file) + : compiler_driver_(&driver), elf_file_(elf_file) {} + +ElfWriter::~ElfWriter() {} + +llvm::ELF::Elf32_Addr ElfWriter::GetOatDataAddress(ElfFile* elf_file) { + llvm::ELF::Elf32_Addr oatdata_address = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM, + "oatdata", + false); + CHECK_NE(0U, oatdata_address); + return oatdata_address; +} + +void ElfWriter::GetOatElfInformation(File* file, + size_t& oat_loaded_size, + size_t& oat_data_offset) { + 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 = GetOatDataAddress(elf_file.get()); + CHECK_NE(0U, oat_data_offset); +} + +} // namespace art diff --git a/src/compiler/elf_writer.h b/src/compiler/elf_writer.h new file mode 100644 index 0000000000..7392e67d7f --- /dev/null +++ b/src/compiler/elf_writer.h @@ -0,0 +1,65 @@ +/* + * 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 <stdint.h> + +#include <cstddef> +#include <string> +#include <vector> + +#include <llvm/Support/ELF.h> + +#include "base/macros.h" +#include "os.h" + +namespace art { + +class CompilerDriver; +class DexFile; +class ElfFile; + +class ElfWriter { + public: + // Looks up information about location of oat file in elf file container. + // Used for ImageWriter to perform memory layout. + static void GetOatElfInformation(File* file, + size_t& oat_loaded_size, + size_t& oat_data_offset); + + // Returns runtime oat_data runtime address for an opened ElfFile. + static llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file); + + protected: + ElfWriter(const CompilerDriver& driver, File* elf_file); + ~ElfWriter(); + + virtual bool Write(std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + + // Setup by constructor + const CompilerDriver* compiler_driver_; + File* elf_file_; +}; + +} // namespace art + +#endif // ART_SRC_ELF_WRITER_H_ diff --git a/src/compiler/elf_writer_mclinker.cc b/src/compiler/elf_writer_mclinker.cc new file mode 100644 index 0000000000..e1d98f6919 --- /dev/null +++ b/src/compiler/elf_writer_mclinker.cc @@ -0,0 +1,401 @@ +/* + * 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_mclinker.h" + +#include <llvm/Support/TargetSelect.h> + +#include <mcld/Environment.h> +#include <mcld/IRBuilder.h> +#include <mcld/Linker.h> +#include <mcld/LinkerConfig.h> +#include <mcld/MC/ZOption.h> +#include <mcld/Module.h> +#include <mcld/Support/Path.h> +#include <mcld/Support/TargetSelect.h> + +#include "base/unix_file/fd_file.h" +#include "class_linker.h" +#include "compiler/driver/compiler_driver.h" +#include "dex_method_iterator.h" +#include "elf_file.h" +#include "globals.h" +#include "mirror/abstract_method.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" + +namespace art { + +ElfWriterMclinker::ElfWriterMclinker(const CompilerDriver& driver, File* elf_file) + : ElfWriter(driver, elf_file), oat_input_(NULL) {} + +ElfWriterMclinker::~ElfWriterMclinker() {} + +bool ElfWriterMclinker::Create(File* elf_file, + std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host, + const CompilerDriver& driver) { + ElfWriterMclinker elf_writer(driver, elf_file); + return elf_writer.Write(oat_contents, dex_files, android_root, is_host); +} + +bool ElfWriterMclinker::Write(std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host) { + Init(); + AddOatInput(oat_contents); +#if defined(ART_USE_PORTABLE_COMPILER) + AddMethodInputs(dex_files); + AddRuntimeInputs(android_root, is_host); +#endif + if (!Link()) { + return false; + } +#if defined(ART_USE_PORTABLE_COMPILER) + FixupOatMethodOffsets(dex_files); +#endif + return true; +} + +static void InitializeLLVM() { + // TODO: this is lifted from art's compiler_llvm.cc, should be factored out + if (kIsTargetBuild) { + llvm::InitializeNativeTarget(); + // TODO: odd that there is no InitializeNativeTargetMC? + } else { + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + } +} + +void ElfWriterMclinker::Init() { + std::string target_triple; + std::string target_cpu; + std::string target_attr; + CompilerDriver::InstructionSetToLLVMTarget(compiler_driver_->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(); + + linker_config_.reset(new mcld::LinkerConfig(target_triple)); + CHECK(linker_config_.get() != NULL); + linker_config_->setCodeGenType(mcld::LinkerConfig::DynObj); + linker_config_->options().setSOName(elf_file_->GetPath()); + + // error on undefined symbols. + // TODO: should this just be set if kIsDebugBuild? + linker_config_->options().setNoUndefined(true); + + if (compiler_driver_->GetInstructionSet() == kMips) { + // MCLinker defaults MIPS section alignment to 0x10000, not + // 0x1000. The ABI says this is because the max page size is + // general is 64k but that isn't true on Android. + mcld::ZOption z_option; + z_option.setKind(mcld::ZOption::MaxPageSize); + z_option.setPageSize(kPageSize); + linker_config_->options().addZOption(z_option); + } + + // TODO: Wire up mcld DiagnosticEngine to LOG? + linker_config_->options().setColor(false); + if (false) { + // enables some tracing of input file processing + linker_config_->options().setTrace(true); + } + + // Based on alone::Linker::config + module_.reset(new mcld::Module(linker_config_->options().soname())); + CHECK(module_.get() != NULL); + ir_builder_.reset(new mcld::IRBuilder(*module_.get(), *linker_config_.get())); + CHECK(ir_builder_.get() != NULL); + linker_.reset(new mcld::Linker()); + CHECK(linker_.get() != NULL); + linker_->config(*linker_config_.get()); +} + +void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) { + // Add an artificial memory input. Based on LinkerTest. + UniquePtr<OatFile> oat_file(OatFile::OpenMemory(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 oat_input? + oat_input_ = ir_builder_->CreateInput("oat contents", + mcld::sys::fs::Path("oat contents path"), + mcld::Input::Object); + CHECK(oat_input_ != NULL); + + // TODO: ownership of null_section? + mcld::LDSection* null_section = ir_builder_->CreateELFHeader(*oat_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 != NULL); + + // we need to ensure that oatdata is page aligned so when we + // fixup the segment load addresses, they remain page aligned. + uint32_t alignment = kPageSize; + + // TODO: ownership of text_section? + mcld::LDSection* text_section = ir_builder_->CreateELFHeader(*oat_input_, + ".text", + llvm::ELF::SHT_PROGBITS, + llvm::ELF::SHF_EXECINSTR + | llvm::ELF::SHF_ALLOC, + alignment); + CHECK(text_section != NULL); + + mcld::SectionData* text_sectiondata = ir_builder_->CreateSectionData(*text_section); + CHECK(text_sectiondata != 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_sectiondata); + + ir_builder_->AddSymbol(*oat_input_, + "oatdata", + mcld::ResolveInfo::Object, + mcld::ResolveInfo::Define, + mcld::ResolveInfo::Global, + oat_data_length, // size + 0, // offset + text_section); + + ir_builder_->AddSymbol(*oat_input_, + "oatexec", + mcld::ResolveInfo::Function, + mcld::ResolveInfo::Define, + mcld::ResolveInfo::Global, + oat_code_length, // size + oat_data_length, // offset + text_section); + + ir_builder_->AddSymbol(*oat_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); +} + +#if defined(ART_USE_PORTABLE_COMPILER) +void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) { + DCHECK(oat_input_ != NULL); + + DexMethodIterator it(dex_files); + while (it.HasNext()) { + const DexFile& dex_file = it.GetDexFile(); + uint32_t method_idx = it.GetMemberIndex(); + const CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx)); + if (compiled_method != NULL) { + AddCompiledCodeInput(*compiled_method); + } + it.Next(); + } + added_symbols_.clear(); +} + +void ElfWriterMclinker::AddCompiledCodeInput(const CompiledCode& compiled_code) { + // Check if we've seen this compiled code before. If so skip + // it. This can happen for reused code such as invoke stubs. + const std::string& symbol = compiled_code.GetSymbol(); + SafeMap<const std::string*, const std::string*>::iterator it = added_symbols_.find(&symbol); + if (it != added_symbols_.end()) { + return; + } + added_symbols_.Put(&symbol, &symbol); + + // Add input to supply code for symbol + const std::vector<uint8_t>& code = compiled_code.GetCode(); + // TODO: ownership of code_input? + // TODO: why does IRBuilder::ReadInput take a non-const pointer? + mcld::Input* code_input = ir_builder_->ReadInput(symbol, + const_cast<uint8_t*>(&code[0]), + code.size()); + CHECK(code_input != NULL); +} + +void ElfWriterMclinker::AddRuntimeInputs(const std::string& android_root, bool is_host) { + std::string libart_so(android_root); + libart_so += kIsDebugBuild ? "/lib/libartd.so" : "/lib/libart.so"; + // TODO: ownership of libart_so_input? + mcld::Input* libart_so_input = ir_builder_->ReadInput(libart_so, libart_so); + CHECK(libart_so_input != NULL); + + std::string host_prebuilt_dir("prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6"); + + std::string compiler_runtime_lib; + if (is_host) { + compiler_runtime_lib += host_prebuilt_dir; + compiler_runtime_lib += "/lib/gcc/i686-linux/4.6.x-google/libgcc.a"; + } else { + compiler_runtime_lib += android_root; + compiler_runtime_lib += "/lib/libcompiler_rt.a"; + } + // TODO: ownership of compiler_runtime_lib_input? + mcld::Input* compiler_runtime_lib_input = ir_builder_->ReadInput(compiler_runtime_lib, + compiler_runtime_lib); + CHECK(compiler_runtime_lib_input != NULL); + + std::string libc_lib; + if (is_host) { + libc_lib += host_prebuilt_dir; + libc_lib += "/sysroot/usr/lib/libc.so.6"; + } else { + libc_lib += android_root; + libc_lib += "/lib/libc.so"; + } + // TODO: ownership of libc_lib_input? + mcld::Input* libc_lib_input_input = ir_builder_->ReadInput(libc_lib, libc_lib); + CHECK(libc_lib_input_input != NULL); + + std::string libm_lib; + if (is_host) { + libm_lib += host_prebuilt_dir; + libm_lib += "/sysroot/usr/lib/libm.so"; + } else { + libm_lib += android_root; + libm_lib += "/lib/libm.so"; + } + // TODO: ownership of libm_lib_input? + mcld::Input* libm_lib_input_input = ir_builder_->ReadInput(libm_lib, libm_lib); + CHECK(libm_lib_input_input != NULL); + +} +#endif + +bool ElfWriterMclinker::Link() { + // link inputs + if (!linker_->link(*module_.get(), *ir_builder_.get())) { + LOG(ERROR) << "Failed to link " << elf_file_->GetPath(); + return false; + } + + // emit linked output + // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd. + int fd = dup(elf_file_->Fd()); + if (fd == -1) { + PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath(); + return false; + } + if (!linker_->emit(fd)) { + LOG(ERROR) << "Failed to emit " << elf_file_->GetPath(); + return false; + } + mcld::Finalize(); + LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath(); + return true; +} + +#if defined(ART_USE_PORTABLE_COMPILER) +void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) { + UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false)); + CHECK(elf_file.get() != NULL) << elf_file_->GetPath(); + + llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get()); + DexMethodIterator it(dex_files); + while (it.HasNext()) { + const DexFile& dex_file = it.GetDexFile(); + uint32_t method_idx = it.GetMemberIndex(); + InvokeType invoke_type = it.GetInvokeType(); + mirror::AbstractMethod* method = NULL; + if (compiler_driver_->IsImage()) { + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + mirror::DexCache* dex_cache = linker->FindDexCache(dex_file); + // Unchecked as we hold mutator_lock_ on entry. + ScopedObjectAccessUnchecked soa(Thread::Current()); + method = linker->ResolveMethod(dex_file, method_idx, dex_cache, NULL, NULL, invoke_type); + CHECK(method != NULL); + } + const CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx)); + if (compiled_method != NULL) { + uint32_t offset = FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_method); + // Don't overwrite static method trampoline + if (method != NULL && + (!method->IsStatic() || + method->IsConstructor() || + method->GetDeclaringClass()->IsInitialized())) { + method->SetOatCodeOffset(offset); + } + } + it.Next(); + } + symbol_to_compiled_code_offset_.clear(); +} + +uint32_t ElfWriterMclinker::FixupCompiledCodeOffset(ElfFile& elf_file, + llvm::ELF::Elf32_Addr oatdata_address, + const CompiledCode& compiled_code) { + const std::string& symbol = compiled_code.GetSymbol(); + SafeMap<const std::string*, uint32_t>::iterator it = symbol_to_compiled_code_offset_.find(&symbol); + if (it != symbol_to_compiled_code_offset_.end()) { + return it->second; + } + + llvm::ELF::Elf32_Addr compiled_code_address = elf_file.FindSymbolAddress(llvm::ELF::SHT_SYMTAB, + symbol, + true); + CHECK_NE(0U, compiled_code_address) << symbol; + CHECK_LT(oatdata_address, compiled_code_address) << symbol; + uint32_t compiled_code_offset = compiled_code_address - oatdata_address; + symbol_to_compiled_code_offset_.Put(&symbol, compiled_code_offset); + + const std::vector<uint32_t>& offsets = compiled_code.GetOatdataOffsetsToCompliledCodeOffset(); + for (uint32_t i = 0; i < offsets.size(); i++) { + uint32_t oatdata_offset = oatdata_address + offsets[i]; + uint32_t* addr = reinterpret_cast<uint32_t*>(elf_file.Begin() + oatdata_offset); + *addr = compiled_code_offset; + } + return compiled_code_offset; +} +#endif + +} // namespace art diff --git a/src/compiler/elf_writer_mclinker.h b/src/compiler/elf_writer_mclinker.h new file mode 100644 index 0000000000..21f23e113d --- /dev/null +++ b/src/compiler/elf_writer_mclinker.h @@ -0,0 +1,99 @@ +/* + * 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_MCLINKER_H_ +#define ART_SRC_ELF_WRITER_MCLINKER_H_ + +#include "elf_writer.h" + +#include "UniquePtr.h" +#include "safe_map.h" + +namespace mcld { +class IRBuilder; +class Input; +class LDSection; +class LDSymbol; +class Linker; +class LinkerConfig; +class Module; +} // namespace mcld + +namespace art { + +class CompiledCode; + +class ElfWriterMclinker : public 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 std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host, + const CompilerDriver& driver) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + protected: + virtual bool Write(std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + ElfWriterMclinker(const CompilerDriver& driver, File* elf_file); + ~ElfWriterMclinker(); + + void Init(); + void AddOatInput(std::vector<uint8_t>& oat_contents); + void AddMethodInputs(const std::vector<const DexFile*>& dex_files); + void AddCompiledCodeInput(const CompiledCode& compiled_code); + void AddRuntimeInputs(const std::string& android_root, bool is_host); + bool Link(); +#if defined(ART_USE_PORTABLE_COMPILER) + void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint32_t FixupCompiledCodeOffset(ElfFile& elf_file, + llvm::ELF::Elf32_Addr oatdata_address, + const CompiledCode& compiled_code); +#endif + + // Setup by Init() + UniquePtr<mcld::LinkerConfig> linker_config_; + UniquePtr<mcld::Module> module_; + UniquePtr<mcld::IRBuilder> ir_builder_; + UniquePtr<mcld::Linker> linker_; + + // Setup by AddOatInput() + // TODO: ownership of oat_input_? + mcld::Input* oat_input_; + + // Setup by AddCompiledCodeInput + // set of symbols for already added mcld::Inputs + SafeMap<const std::string*, const std::string*> added_symbols_; + + // Setup by FixupCompiledCodeOffset + // map of symbol names to oatdata offset + SafeMap<const std::string*, uint32_t> symbol_to_compiled_code_offset_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterMclinker); +}; + +} // namespace art + +#endif // ART_SRC_ELF_WRITER_MCLINKER_H_ diff --git a/src/compiler/elf_writer_quick.cc b/src/compiler/elf_writer_quick.cc new file mode 100644 index 0000000000..c3c5415733 --- /dev/null +++ b/src/compiler/elf_writer_quick.cc @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "elf_writer_quick.h" + +#include "base/logging.h" +#include "base/unix_file/fd_file.h" +#include "compiler/driver/compiler_driver.h" +#include "globals.h" +#include "oat.h" +#include "utils.h" + +namespace art { + +ElfWriterQuick::ElfWriterQuick(const CompilerDriver& driver, File* elf_file) + : ElfWriter(driver, elf_file) {} + +ElfWriterQuick::~ElfWriterQuick() {} + +bool ElfWriterQuick::Create(File* elf_file, + std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host, + const CompilerDriver& driver) { + ElfWriterQuick elf_writer(driver, elf_file); + return elf_writer.Write(oat_contents, dex_files, android_root, is_host); +} + +bool ElfWriterQuick::Write(std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files_unused, + const std::string& android_root_unused, + bool is_host_unused) { + const bool debug = false; + // +-------------------------+ + // | Elf32_Ehdr | + // +-------------------------+ + // | Elf32_Phdr PHDR | + // | Elf32_Phdr LOAD R | .dynsym .dynstr .hash .rodata + // | Elf32_Phdr LOAD R X | .text + // | Elf32_Phdr LOAD RW | .dynamic + // | Elf32_Phdr DYNAMIC | .dynamic + // +-------------------------+ + // | .dynsym | + // | Elf32_Sym STN_UNDEF | + // | Elf32_Sym oatdata | + // | Elf32_Sym oatexec | + // | Elf32_Sym oatlastword | + // +-------------------------+ + // | .dynstr | + // | \0 | + // | oatdata\0 | + // | oatexec\0 | + // | oatlastword\0 | + // | boot.oat\0 | + // +-------------------------+ + // | .hash | + // | Elf32_Word nbucket = 1 | + // | Elf32_Word nchain = 3 | + // | Elf32_Word bucket[0] = 0| + // | Elf32_Word chain[0] = 1| + // | Elf32_Word chain[1] = 2| + // | Elf32_Word chain[2] = 3| + // +-------------------------+ + // | .rodata | + // | oatdata..oatexec-4 | + // +-------------------------+ + // | .text | + // | oatexec..oatlastword | + // +-------------------------+ + // | .dynamic | + // | Elf32_Dyn DT_SONAME | + // | Elf32_Dyn DT_HASH | + // | Elf32_Dyn DT_SYMTAB | + // | Elf32_Dyn DT_SYMENT | + // | Elf32_Dyn DT_STRTAB | + // | Elf32_Dyn DT_STRSZ | + // | Elf32_Dyn DT_NULL | + // +-------------------------+ + // | .shstrtab | + // | \0 | + // | .dynamic\0 | + // | .dynsym\0 | + // | .dynstr\0 | + // | .hash\0 | + // | .rodata\0 | + // | .text\0 | + // | .shstrtab\0 | + // +-------------------------+ + // | Elf32_Shdr NULL | + // | Elf32_Shdr .dynsym | + // | Elf32_Shdr .dynstr | + // | Elf32_Shdr .hash | + // | Elf32_Shdr .text | + // | Elf32_Shdr .rodata | + // | Elf32_Shdr .dynamic | + // | Elf32_Shdr .shstrtab | + // +-------------------------+ + + // phase 1: computing offsets + uint32_t expected_offset = 0; + + // Elf32_Ehdr + expected_offset += sizeof(llvm::ELF::Elf32_Ehdr); + + // PHDR + uint32_t phdr_alignment = sizeof(llvm::ELF::Elf32_Word); + uint32_t phdr_offset = expected_offset; + const uint8_t PH_PHDR = 0; + const uint8_t PH_LOAD_R__ = 1; + const uint8_t PH_LOAD_R_X = 2; + const uint8_t PH_LOAD_RW_ = 3; + const uint8_t PH_DYNAMIC = 4; + const uint8_t PH_NUM = 5; + uint32_t phdr_size = sizeof(llvm::ELF::Elf32_Phdr) * PH_NUM; + expected_offset += phdr_size; + if (debug) { + LOG(INFO) << "phdr_offset=" << phdr_offset << std::hex << " " << phdr_offset; + LOG(INFO) << "phdr_size=" << phdr_size << std::hex << " " << phdr_size; + } + + // .dynsym + uint32_t dynsym_alignment = sizeof(llvm::ELF::Elf32_Word); + uint32_t dynsym_offset = expected_offset = RoundUp(expected_offset, dynsym_alignment); + const uint8_t SYM_UNDEF = 0; // aka STN_UNDEF + const uint8_t SYM_OATDATA = 1; + const uint8_t SYM_OATEXEC = 2; + const uint8_t SYM_OATLASTWORD = 3; + const uint8_t SYM_NUM = 4; + uint32_t dynsym_size = sizeof(llvm::ELF::Elf32_Sym) * SYM_NUM; + expected_offset += dynsym_size; + if (debug) { + LOG(INFO) << "dynsym_offset=" << dynsym_offset << std::hex << " " << dynsym_offset; + LOG(INFO) << "dynsym_size=" << dynsym_size << std::hex << " " << dynsym_size; + } + + // .dynstr + uint32_t dynstr_alignment = 1; + uint32_t dynstr_offset = expected_offset = RoundUp(expected_offset, dynstr_alignment); + std::string dynstr; + dynstr += '\0'; + uint32_t dynstr_oatdata_offset = dynstr.size(); + dynstr += "oatdata"; + dynstr += '\0'; + uint32_t dynstr_oatexec_offset = dynstr.size(); + dynstr += "oatexec"; + dynstr += '\0'; + uint32_t dynstr_oatlastword_offset = dynstr.size(); + dynstr += "oatlastword"; + dynstr += '\0'; + uint32_t dynstr_soname_offset = dynstr.size(); + std::string file_name(elf_file_->GetPath()); + size_t directory_separator_pos = file_name.rfind('/'); + if (directory_separator_pos != std::string::npos) { + file_name = file_name.substr(directory_separator_pos + 1); + } + dynstr += file_name; + dynstr += '\0'; + uint32_t dynstr_size = dynstr.size(); + expected_offset += dynstr_size; + if (debug) { + LOG(INFO) << "dynstr_offset=" << dynstr_offset << std::hex << " " << dynstr_offset; + LOG(INFO) << "dynstr_size=" << dynstr_size << std::hex << " " << dynstr_size; + } + + // .hash + uint32_t hash_alignment = sizeof(llvm::ELF::Elf32_Word); // Even for 64-bit + uint32_t hash_offset = expected_offset = RoundUp(expected_offset, hash_alignment); + const uint8_t HASH_NBUCKET = 0; + const uint8_t HASH_NCHAIN = 1; + const uint8_t HASH_BUCKET0 = 2; + const uint8_t HASH_NUM = HASH_BUCKET0 + 1 + SYM_NUM; + uint32_t hash_size = sizeof(llvm::ELF::Elf32_Word) * HASH_NUM; + expected_offset += hash_size; + if (debug) { + LOG(INFO) << "hash_offset=" << hash_offset << std::hex << " " << hash_offset; + LOG(INFO) << "hash_size=" << hash_size << std::hex << " " << hash_size; + } + + // .rodata + uint32_t oat_data_alignment = kPageSize; + uint32_t oat_data_offset = expected_offset = RoundUp(expected_offset, oat_data_alignment); + const OatHeader* oat_header = reinterpret_cast<OatHeader*>(&oat_contents[0]); + CHECK(oat_header->IsValid()); + uint32_t oat_data_size = oat_header->GetExecutableOffset(); + expected_offset += oat_data_size; + if (debug) { + LOG(INFO) << "oat_data_offset=" << oat_data_offset << std::hex << " " << oat_data_offset; + LOG(INFO) << "oat_data_size=" << oat_data_size << std::hex << " " << oat_data_size; + } + + // .text + uint32_t oat_exec_alignment = kPageSize; + CHECK_ALIGNED(expected_offset, kPageSize); + uint32_t oat_exec_offset = expected_offset = RoundUp(expected_offset, oat_exec_alignment); + uint32_t oat_exec_size = oat_contents.size() - oat_data_size; + expected_offset += oat_exec_size; + CHECK_EQ(oat_data_offset + oat_contents.size(), expected_offset); + if (debug) { + LOG(INFO) << "oat_exec_offset=" << oat_exec_offset << std::hex << " " << oat_exec_offset; + LOG(INFO) << "oat_exec_size=" << oat_exec_size << std::hex << " " << oat_exec_size; + } + + // .dynamic + // alignment would naturally be sizeof(llvm::ELF::Elf32_Word), but we want this in a new segment + uint32_t dynamic_alignment = kPageSize; + uint32_t dynamic_offset = expected_offset = RoundUp(expected_offset, dynamic_alignment); + const uint8_t DH_SONAME = 0; + const uint8_t DH_HASH = 1; + const uint8_t DH_SYMTAB = 2; + const uint8_t DH_SYMENT = 3; + const uint8_t DH_STRTAB = 4; + const uint8_t DH_STRSZ = 5; + const uint8_t DH_NULL = 6; + const uint8_t DH_NUM = 7; + uint32_t dynamic_size = sizeof(llvm::ELF::Elf32_Dyn) * DH_NUM; + expected_offset += dynamic_size; + if (debug) { + LOG(INFO) << "dynamic_offset=" << dynamic_offset << std::hex << " " << dynamic_offset; + LOG(INFO) << "dynamic_size=" << dynamic_size << std::hex << " " << dynamic_size; + } + + // .shstrtab + uint32_t shstrtab_alignment = 1; + uint32_t shstrtab_offset = expected_offset = RoundUp(expected_offset, shstrtab_alignment); + std::string shstrtab; + shstrtab += '\0'; + uint32_t shstrtab_dynamic_offset = shstrtab.size(); + CHECK_EQ(1U, shstrtab_dynamic_offset); + shstrtab += ".dynamic"; + shstrtab += '\0'; + uint32_t shstrtab_dynsym_offset = shstrtab.size(); + shstrtab += ".dynsym"; + shstrtab += '\0'; + uint32_t shstrtab_dynstr_offset = shstrtab.size(); + shstrtab += ".dynstr"; + shstrtab += '\0'; + uint32_t shstrtab_hash_offset = shstrtab.size(); + shstrtab += ".hash"; + shstrtab += '\0'; + uint32_t shstrtab_rodata_offset = shstrtab.size(); + shstrtab += ".rodata"; + shstrtab += '\0'; + uint32_t shstrtab_text_offset = shstrtab.size(); + shstrtab += ".text"; + shstrtab += '\0'; + uint32_t shstrtab_shstrtab_offset = shstrtab.size(); + shstrtab += ".shstrtab"; + shstrtab += '\0'; + uint32_t shstrtab_size = shstrtab.size(); + expected_offset += shstrtab_size; + if (debug) { + LOG(INFO) << "shstrtab_offset=" << shstrtab_offset << std::hex << " " << shstrtab_offset; + LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size; + } + + // section headers (after all sections) + uint32_t shdr_alignment = sizeof(llvm::ELF::Elf32_Word); + uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment); + const uint8_t SH_NULL = 0; + const uint8_t SH_DYNSYM = 1; + const uint8_t SH_DYNSTR = 2; + const uint8_t SH_HASH = 3; + const uint8_t SH_RODATA = 4; + const uint8_t SH_TEXT = 5; + const uint8_t SH_DYNAMIC = 6; + const uint8_t SH_SHSTRTAB = 7; + const uint8_t SH_NUM = 8; + uint32_t shdr_size = sizeof(llvm::ELF::Elf32_Shdr) * SH_NUM; + expected_offset += shdr_size; + if (debug) { + LOG(INFO) << "shdr_offset=" << shdr_offset << std::hex << " " << shdr_offset; + LOG(INFO) << "shdr_size=" << shdr_size << std::hex << " " << shdr_size; + } + + // phase 2: initializing data + + // Elf32_Ehdr + llvm::ELF::Elf32_Ehdr elf_header; + memset(&elf_header, 0, sizeof(elf_header)); + elf_header.e_ident[llvm::ELF::EI_MAG0] = llvm::ELF::ElfMagic[0]; + elf_header.e_ident[llvm::ELF::EI_MAG1] = llvm::ELF::ElfMagic[1]; + elf_header.e_ident[llvm::ELF::EI_MAG2] = llvm::ELF::ElfMagic[2]; + elf_header.e_ident[llvm::ELF::EI_MAG3] = llvm::ELF::ElfMagic[3]; + elf_header.e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS32; + elf_header.e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB; + elf_header.e_ident[llvm::ELF::EI_VERSION] = llvm::ELF::EV_CURRENT; + elf_header.e_ident[llvm::ELF::EI_OSABI] = llvm::ELF::ELFOSABI_LINUX; + elf_header.e_ident[llvm::ELF::EI_ABIVERSION] = 0; + elf_header.e_type = llvm::ELF::ET_DYN; + switch (compiler_driver_->GetInstructionSet()) { + case kThumb2: { + elf_header.e_machine = llvm::ELF::EM_ARM; + elf_header.e_flags = llvm::ELF::EF_ARM_EABI_VER5; + break; + } + case kX86: { + elf_header.e_machine = llvm::ELF::EM_386; + elf_header.e_flags = 0; + break; + } + case kMips: { + elf_header.e_machine = llvm::ELF::EM_MIPS; + elf_header.e_flags = (llvm::ELF::EF_MIPS_NOREORDER | + llvm::ELF::EF_MIPS_PIC | + llvm::ELF::EF_MIPS_CPIC | + llvm::ELF::EF_MIPS_ABI_O32 | + llvm::ELF::EF_MIPS_ARCH_32R2); + break; + } + case kArm: + default: { + LOG(FATAL) << "Unknown instruction set: " << compiler_driver_->GetInstructionSet(); + break; + } + } + elf_header.e_version = 1; + elf_header.e_entry = 0; + elf_header.e_phoff = phdr_offset; + elf_header.e_shoff = shdr_offset; + elf_header.e_ehsize = sizeof(llvm::ELF::Elf32_Ehdr); + elf_header.e_phentsize = sizeof(llvm::ELF::Elf32_Phdr); + elf_header.e_phnum = PH_NUM; + elf_header.e_shentsize = sizeof(llvm::ELF::Elf32_Shdr); + elf_header.e_shnum = SH_NUM; + elf_header.e_shstrndx = SH_SHSTRTAB; + + // PHDR + llvm::ELF::Elf32_Phdr program_headers[PH_NUM]; + memset(&program_headers, 0, sizeof(program_headers)); + + program_headers[PH_PHDR].p_type = llvm::ELF::PT_PHDR; + program_headers[PH_PHDR].p_offset = phdr_offset; + program_headers[PH_PHDR].p_vaddr = phdr_offset; + program_headers[PH_PHDR].p_paddr = phdr_offset; + program_headers[PH_PHDR].p_filesz = sizeof(program_headers); + program_headers[PH_PHDR].p_memsz = sizeof(program_headers); + program_headers[PH_PHDR].p_flags = llvm::ELF::PF_R; + program_headers[PH_PHDR].p_align = phdr_alignment; + + program_headers[PH_LOAD_R__].p_type = llvm::ELF::PT_LOAD; + program_headers[PH_LOAD_R__].p_offset = 0; + program_headers[PH_LOAD_R__].p_vaddr = 0; + program_headers[PH_LOAD_R__].p_paddr = 0; + program_headers[PH_LOAD_R__].p_filesz = oat_data_offset + oat_data_size; + program_headers[PH_LOAD_R__].p_memsz = oat_data_offset + oat_data_size; + program_headers[PH_LOAD_R__].p_flags = llvm::ELF::PF_R; + program_headers[PH_LOAD_R__].p_align = oat_data_alignment; + + program_headers[PH_LOAD_R_X].p_type = llvm::ELF::PT_LOAD; + program_headers[PH_LOAD_R_X].p_offset = oat_exec_offset; + program_headers[PH_LOAD_R_X].p_vaddr = oat_exec_offset; + program_headers[PH_LOAD_R_X].p_paddr = oat_exec_offset; + program_headers[PH_LOAD_R_X].p_filesz = oat_exec_size; + program_headers[PH_LOAD_R_X].p_memsz = oat_exec_size; + program_headers[PH_LOAD_R_X].p_flags = llvm::ELF::PF_R | llvm::ELF::PF_X; + program_headers[PH_LOAD_R_X].p_align = oat_exec_alignment; + + // TODO: PF_W for DYNAMIC is considered processor specific, do we need it? + program_headers[PH_LOAD_RW_].p_type = llvm::ELF::PT_LOAD; + program_headers[PH_LOAD_RW_].p_offset = dynamic_offset; + program_headers[PH_LOAD_RW_].p_vaddr = dynamic_offset; + program_headers[PH_LOAD_RW_].p_paddr = dynamic_offset; + program_headers[PH_LOAD_RW_].p_filesz = dynamic_size; + program_headers[PH_LOAD_RW_].p_memsz = dynamic_size; + program_headers[PH_LOAD_RW_].p_flags = llvm::ELF::PF_R | llvm::ELF::PF_W; + program_headers[PH_LOAD_RW_].p_align = dynamic_alignment; + + // TODO: PF_W for DYNAMIC is considered processor specific, do we need it? + program_headers[PH_DYNAMIC].p_type = llvm::ELF::PT_DYNAMIC; + program_headers[PH_DYNAMIC].p_offset = dynamic_offset; + program_headers[PH_DYNAMIC].p_vaddr = dynamic_offset; + program_headers[PH_DYNAMIC].p_paddr = dynamic_offset; + program_headers[PH_DYNAMIC].p_filesz = dynamic_size; + program_headers[PH_DYNAMIC].p_memsz = dynamic_size; + program_headers[PH_DYNAMIC].p_flags = llvm::ELF::PF_R | llvm::ELF::PF_W; + program_headers[PH_DYNAMIC].p_align = dynamic_alignment; + + // .dynsym + llvm::ELF::Elf32_Sym dynsym[SYM_NUM]; + memset(&dynsym, 0, sizeof(dynsym)); + + dynsym[SYM_UNDEF].st_name = 0; + dynsym[SYM_UNDEF].st_value = 0; + dynsym[SYM_UNDEF].st_size = 0; + dynsym[SYM_UNDEF].st_info = 0; + dynsym[SYM_UNDEF].st_other = 0; + dynsym[SYM_UNDEF].st_shndx = 0; + + dynsym[SYM_OATDATA].st_name = dynstr_oatdata_offset; + dynsym[SYM_OATDATA].st_value = oat_data_offset; + dynsym[SYM_OATDATA].st_size = oat_data_size; + dynsym[SYM_OATDATA].setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + dynsym[SYM_OATDATA].st_other = llvm::ELF::STV_DEFAULT; + dynsym[SYM_OATDATA].st_shndx = SH_RODATA; + + dynsym[SYM_OATEXEC].st_name = dynstr_oatexec_offset; + dynsym[SYM_OATEXEC].st_value = oat_exec_offset; + dynsym[SYM_OATEXEC].st_size = oat_exec_size; + dynsym[SYM_OATEXEC].setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + dynsym[SYM_OATEXEC].st_other = llvm::ELF::STV_DEFAULT; + dynsym[SYM_OATEXEC].st_shndx = SH_TEXT; + + dynsym[SYM_OATLASTWORD].st_name = dynstr_oatlastword_offset; + dynsym[SYM_OATLASTWORD].st_value = oat_exec_offset + oat_exec_size - 4; + dynsym[SYM_OATLASTWORD].st_size = 4; + dynsym[SYM_OATLASTWORD].setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + dynsym[SYM_OATLASTWORD].st_other = llvm::ELF::STV_DEFAULT; + dynsym[SYM_OATLASTWORD].st_shndx = SH_TEXT; + + // .dynstr initialized above as dynstr + + // .hash + llvm::ELF::Elf32_Word hash[HASH_NUM]; // Note this is Elf32_Word even on 64-bit + hash[HASH_NBUCKET] = 1; + hash[HASH_NCHAIN] = SYM_NUM; + hash[HASH_BUCKET0] = SYM_OATDATA; + hash[HASH_BUCKET0 + 1 + SYM_UNDEF] = SYM_UNDEF; + hash[HASH_BUCKET0 + 1 + SYM_OATDATA] = SYM_OATEXEC; + hash[HASH_BUCKET0 + 1 + SYM_OATEXEC] = SYM_OATLASTWORD; + hash[HASH_BUCKET0 + 1 + SYM_OATLASTWORD] = SYM_UNDEF; + + // .rodata and .text content come from oat_contents + + // .dynamic + llvm::ELF::Elf32_Dyn dynamic_headers[DH_NUM]; + memset(&dynamic_headers, 0, sizeof(dynamic_headers)); + + dynamic_headers[DH_SONAME].d_tag = llvm::ELF::DT_SONAME; + dynamic_headers[DH_SONAME].d_un.d_val = dynstr_soname_offset; + + dynamic_headers[DH_HASH].d_tag = llvm::ELF::DT_HASH; + dynamic_headers[DH_HASH].d_un.d_ptr = hash_offset; + + dynamic_headers[DH_SYMTAB].d_tag = llvm::ELF::DT_SYMTAB; + dynamic_headers[DH_SYMTAB].d_un.d_ptr = dynsym_offset; + + dynamic_headers[DH_SYMENT].d_tag = llvm::ELF::DT_SYMENT; + dynamic_headers[DH_SYMENT].d_un.d_val = sizeof(llvm::ELF::Elf32_Sym); + + dynamic_headers[DH_STRTAB].d_tag = llvm::ELF::DT_STRTAB; + dynamic_headers[DH_STRTAB].d_un.d_ptr = dynstr_offset; + + dynamic_headers[DH_STRSZ].d_tag = llvm::ELF::DT_STRSZ; + dynamic_headers[DH_STRSZ].d_un.d_val = dynstr_size; + + dynamic_headers[DH_NULL].d_tag = llvm::ELF::DT_NULL; + dynamic_headers[DH_NULL].d_un.d_val = 0; + + // .shstrtab initialized above as shstrtab + + // section headers (after all sections) + llvm::ELF::Elf32_Shdr section_headers[SH_NUM]; + memset(§ion_headers, 0, sizeof(section_headers)); + + section_headers[SH_NULL].sh_name = 0; + section_headers[SH_NULL].sh_type = llvm::ELF::SHT_NULL; + section_headers[SH_NULL].sh_flags = 0; + section_headers[SH_NULL].sh_addr = 0; + section_headers[SH_NULL].sh_offset = 0; + section_headers[SH_NULL].sh_size = 0; + section_headers[SH_NULL].sh_link = 0; + section_headers[SH_NULL].sh_info = 0; + section_headers[SH_NULL].sh_addralign = 0; + section_headers[SH_NULL].sh_entsize = 0; + + section_headers[SH_DYNSYM].sh_name = shstrtab_dynsym_offset; + section_headers[SH_DYNSYM].sh_type = llvm::ELF::SHT_DYNSYM; + section_headers[SH_DYNSYM].sh_flags = llvm::ELF::SHF_ALLOC; + section_headers[SH_DYNSYM].sh_addr = dynsym_offset; + section_headers[SH_DYNSYM].sh_offset = dynsym_offset; + section_headers[SH_DYNSYM].sh_size = dynsym_size; + section_headers[SH_DYNSYM].sh_link = SH_DYNSTR; + section_headers[SH_DYNSYM].sh_info = 1; // 1 because we have not STB_LOCAL symbols + section_headers[SH_DYNSYM].sh_addralign = dynsym_alignment; + section_headers[SH_DYNSYM].sh_entsize = sizeof(llvm::ELF::Elf32_Sym); + + section_headers[SH_DYNSTR].sh_name = shstrtab_dynstr_offset; + section_headers[SH_DYNSTR].sh_type = llvm::ELF::SHT_STRTAB; + section_headers[SH_DYNSTR].sh_flags = llvm::ELF::SHF_ALLOC; + section_headers[SH_DYNSTR].sh_addr = dynstr_offset; + section_headers[SH_DYNSTR].sh_offset = dynstr_offset; + section_headers[SH_DYNSTR].sh_size = dynstr_size; + section_headers[SH_DYNSTR].sh_link = 0; + section_headers[SH_DYNSTR].sh_info = 0; + section_headers[SH_DYNSTR].sh_addralign = dynstr_alignment; + section_headers[SH_DYNSTR].sh_entsize = 0; + + section_headers[SH_HASH].sh_name = shstrtab_hash_offset; + section_headers[SH_HASH].sh_type = llvm::ELF::SHT_HASH; + section_headers[SH_HASH].sh_flags = llvm::ELF::SHF_ALLOC; + section_headers[SH_HASH].sh_addr = hash_offset; + section_headers[SH_HASH].sh_offset = hash_offset; + section_headers[SH_HASH].sh_size = hash_size; + section_headers[SH_HASH].sh_link = SH_DYNSYM; + section_headers[SH_HASH].sh_info = 0; + section_headers[SH_HASH].sh_addralign = hash_alignment; + section_headers[SH_HASH].sh_entsize = sizeof(llvm::ELF::Elf32_Word); // This is Elf32_Word even on 64-bit + + section_headers[SH_RODATA].sh_name = shstrtab_rodata_offset; + section_headers[SH_RODATA].sh_type = llvm::ELF::SHT_PROGBITS; + section_headers[SH_RODATA].sh_flags = llvm::ELF::SHF_ALLOC; + section_headers[SH_RODATA].sh_addr = oat_data_offset; + section_headers[SH_RODATA].sh_offset = oat_data_offset; + section_headers[SH_RODATA].sh_size = oat_data_size; + section_headers[SH_RODATA].sh_link = 0; + section_headers[SH_RODATA].sh_info = 0; + section_headers[SH_RODATA].sh_addralign = oat_data_alignment; + section_headers[SH_RODATA].sh_entsize = 0; + + section_headers[SH_TEXT].sh_name = shstrtab_text_offset; + section_headers[SH_TEXT].sh_type = llvm::ELF::SHT_PROGBITS; + section_headers[SH_TEXT].sh_flags = llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR; + section_headers[SH_TEXT].sh_addr = oat_exec_offset; + section_headers[SH_TEXT].sh_offset = oat_exec_offset; + section_headers[SH_TEXT].sh_size = oat_exec_size; + section_headers[SH_TEXT].sh_link = 0; + section_headers[SH_TEXT].sh_info = 0; + section_headers[SH_TEXT].sh_addralign = oat_exec_alignment; + section_headers[SH_TEXT].sh_entsize = 0; + + // TODO: SHF_WRITE for .dynamic is considered processor specific, do we need it? + section_headers[SH_DYNAMIC].sh_name = shstrtab_dynamic_offset; + section_headers[SH_DYNAMIC].sh_type = llvm::ELF::SHT_DYNAMIC; + section_headers[SH_DYNAMIC].sh_flags = llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC; + section_headers[SH_DYNAMIC].sh_addr = dynamic_offset; + section_headers[SH_DYNAMIC].sh_offset = dynamic_offset; + section_headers[SH_DYNAMIC].sh_size = dynamic_size; + section_headers[SH_DYNAMIC].sh_link = SH_DYNSTR; + section_headers[SH_DYNAMIC].sh_info = 0; + section_headers[SH_DYNAMIC].sh_addralign = dynamic_alignment; + section_headers[SH_DYNAMIC].sh_entsize = sizeof(llvm::ELF::Elf32_Dyn); + + section_headers[SH_SHSTRTAB].sh_name = shstrtab_shstrtab_offset; + section_headers[SH_SHSTRTAB].sh_type = llvm::ELF::SHT_STRTAB; + section_headers[SH_SHSTRTAB].sh_flags = 0; + section_headers[SH_SHSTRTAB].sh_addr = shstrtab_offset; + section_headers[SH_SHSTRTAB].sh_offset = shstrtab_offset; + section_headers[SH_SHSTRTAB].sh_size = shstrtab_size; + section_headers[SH_SHSTRTAB].sh_link = 0; + section_headers[SH_SHSTRTAB].sh_info = 0; + section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment; + section_headers[SH_SHSTRTAB].sh_entsize = 0; + + // phase 3: writing file + + // Elf32_Ehdr + if (!elf_file_->WriteFully(&elf_header, sizeof(elf_header))) { + PLOG(ERROR) << "Failed to write ELF header for " << elf_file_->GetPath(); + return false; + } + + // PHDR + if (static_cast<off_t>(phdr_offset) != lseek(elf_file_->Fd(), 0, SEEK_CUR)) { + PLOG(ERROR) << "Failed to be at expected ELF program header offset phdr_offset " + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(program_headers, sizeof(program_headers))) { + PLOG(ERROR) << "Failed to write ELF program headers for " << elf_file_->GetPath(); + return false; + } + + // .dynsym + DCHECK_LE(phdr_offset + phdr_size, dynsym_offset); + if (static_cast<off_t>(dynsym_offset) != lseek(elf_file_->Fd(), dynsym_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .dynsym offset location " << dynsym_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(dynsym, sizeof(dynsym))) { + PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath(); + return false; + } + + // .dynstr + DCHECK_LE(dynsym_offset + dynsym_size, dynstr_offset); + if (static_cast<off_t>(dynstr_offset) != lseek(elf_file_->Fd(), dynstr_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .dynstr offset " << dynstr_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dynstr[0], dynsym_size)) { + PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath(); + return false; + } + + // .hash + DCHECK_LE(dynstr_offset + dynstr_size, hash_offset); + if (static_cast<off_t>(hash_offset) != lseek(elf_file_->Fd(), hash_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .hash offset " << hash_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(hash, sizeof(hash))) { + PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath(); + return false; + } + + // .rodata .text + DCHECK_LE(hash_offset + hash_size, oat_data_offset); + if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&oat_contents[0], oat_contents.size())) { + PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath(); + return false; + } + + // .dynamic + DCHECK_LE(oat_data_offset + oat_contents.size(), dynamic_offset); + if (static_cast<off_t>(dynamic_offset) != lseek(elf_file_->Fd(), dynamic_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .dynamic offset " << dynamic_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dynamic_headers[0], dynamic_size)) { + PLOG(ERROR) << "Failed to write .dynamic for " << elf_file_->GetPath(); + return false; + } + + // .shstrtab + DCHECK_LE(dynamic_offset + dynamic_size, shstrtab_offset); + if (static_cast<off_t>(shstrtab_offset) != lseek(elf_file_->Fd(), shstrtab_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shstrtab offset " << shstrtab_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&shstrtab[0], shstrtab_size)) { + PLOG(ERROR) << "Failed to write .shstrtab for " << elf_file_->GetPath(); + return false; + } + + // section headers (after all sections) + DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset); + if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(section_headers, sizeof(section_headers))) { + PLOG(ERROR) << "Failed to write ELF section headers for " << elf_file_->GetPath(); + return false; + } + + LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath(); + return true; +} + +} // namespace art diff --git a/src/compiler/elf_writer_quick.h b/src/compiler/elf_writer_quick.h new file mode 100644 index 0000000000..a1a386b3d7 --- /dev/null +++ b/src/compiler/elf_writer_quick.h @@ -0,0 +1,51 @@ +/* + * 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_MCLINKER_H_ +#define ART_SRC_ELF_WRITER_MCLINKER_H_ + +#include "elf_writer.h" + +namespace art { + +class ElfWriterQuick : public 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 std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host, + const CompilerDriver& driver) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + protected: + virtual bool Write(std::vector<uint8_t>& oat_contents, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + ElfWriterQuick(const CompilerDriver& driver, File* elf_file); + ~ElfWriterQuick(); + + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick); +}; + +} // namespace art + +#endif // ART_SRC_ELF_WRITER_MCLINKER_H_ diff --git a/src/compiler/elf_writer_test.cc b/src/compiler/elf_writer_test.cc new file mode 100644 index 0000000000..d4486d2c56 --- /dev/null +++ b/src/compiler/elf_writer_test.cc @@ -0,0 +1,92 @@ +/* + * 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, build_map) \ + EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(::llvm::ELF::SHT_DYNSYM, name, build_map))); \ + 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", false); + EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false); + EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", false); + } + { + UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false)); + CHECK(ef.get() != NULL); + EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true); + EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true); + EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true); + } + { + 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/compiler/image_writer.cc b/src/compiler/image_writer.cc new file mode 100644 index 0000000000..8b84e124e7 --- /dev/null +++ b/src/compiler/image_writer.cc @@ -0,0 +1,680 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "image_writer.h" + +#include <sys/stat.h> + +#include <vector> + +#include "base/logging.h" +#include "base/unix_file/fd_file.h" +#include "class_linker.h" +#include "compiled_method.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler/elf_writer.h" +#include "dex_file-inl.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/heap.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" +#include "globals.h" +#include "image.h" +#include "intern_table.h" +#include "mirror/array-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache-inl.h" +#include "mirror/field-inl.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "oat.h" +#include "oat_file.h" +#include "object_utils.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "sirt_ref.h" +#include "UniquePtr.h" +#include "utils.h" + +using namespace art::mirror; + +namespace art { + +bool ImageWriter::Write(const std::string& image_filename, + uintptr_t image_begin, + const std::string& oat_filename, + const std::string& oat_location) { + CHECK(!image_filename.empty()); + + CHECK_NE(image_begin, 0U); + image_begin_ = reinterpret_cast<byte*>(image_begin); + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const std::vector<DexCache*>& all_dex_caches = class_linker->GetDexCaches(); + for (size_t i = 0; i < all_dex_caches.size(); i++) { + DexCache* dex_cache = all_dex_caches[i]; + dex_caches_.insert(dex_cache); + } + + 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::OpenWritable(oat_file.get(), oat_location); + class_linker->RegisterOatFile(*oat_file_); + interpreter_to_interpreter_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterEntryOffset(); + interpreter_to_quick_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToQuickEntryOffset(); + portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset(); + quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset(); + + { + Thread::Current()->TransitionFromSuspendedToRunnable(); + PruneNonImageClasses(); // Remove junk + ComputeLazyFieldsForImageClasses(); // Add useful information + ComputeEagerResolvedStrings(); + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + heap->CollectGarbage(false); // Remove garbage. + // Trim size of alloc spaces. + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + space->AsDlMallocSpace()->Trim(); + } + } + + if (!AllocMemory()) { + return false; + } +#ifndef NDEBUG + { + ScopedObjectAccess soa(Thread::Current()); + CheckNonImageClassesRemoved(); + } +#endif + Thread::Current()->TransitionFromSuspendedToRunnable(); + size_t oat_loaded_size = 0; + size_t oat_data_offset = 0; + ElfWriter::GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); + CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); + CopyAndFixupObjects(); + PatchOatCodeAndMethods(); + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + + 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(image_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; + return EXIT_FAILURE; + } + bool success = image_file->WriteFully(image_->Begin(), image_end_); + if (!success) { + PLOG(ERROR) << "Failed to write image file " << image_filename; + return false; + } + return true; +} + +bool ImageWriter::AllocMemory() { + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + size_t size = 0; + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + if (space->IsDlMallocSpace()) { + size += space->Size(); + } + } + + int prot = PROT_READ | PROT_WRITE; + size_t length = RoundUp(size, kPageSize); + image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot)); + if (image_.get() == NULL) { + LOG(ERROR) << "Failed to allocate memory for image file generation"; + return false; + } + return true; +} + +void ImageWriter::ComputeLazyFieldsForImageClasses() { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, NULL); +} + +bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) { + c->ComputeName(); + return true; +} + +void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { + if (!obj->GetClass()->IsStringClass()) { + return; + } + String* string = obj->AsString(); + const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); + ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); + typedef Set::const_iterator CacheIt; // TODO: C++0x auto + for (CacheIt it = writer->dex_caches_.begin(), end = writer->dex_caches_.end(); it != end; ++it) { + DexCache* dex_cache = *it; + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); + if (string_id != NULL) { + // This string occurs in this dex file, assign the dex cache entry. + uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); + if (dex_cache->GetResolvedString(string_idx) == NULL) { + dex_cache->SetResolvedString(string_idx, string); + } + } + } +} + +void ImageWriter::ComputeEagerResolvedStrings() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // TODO: Check image spaces only? + gc::Heap* heap = Runtime::Current()->GetHeap(); + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + heap->GetLiveBitmap()->Walk(ComputeEagerResolvedStringsCallback, this); +} + +bool ImageWriter::IsImageClass(const Class* klass) { + return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptor()); +} + +struct NonImageClasses { + ImageWriter* image_writer; + std::set<std::string>* non_image_classes; +}; + +void ImageWriter::PruneNonImageClasses() { + if (compiler_driver_.GetImageClasses() == NULL) { + return; + } + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + + // Make a list of classes we would like to prune. + std::set<std::string> non_image_classes; + NonImageClasses context; + context.image_writer = this; + context.non_image_classes = &non_image_classes; + class_linker->VisitClasses(NonImageClassesVisitor, &context); + + // Remove the undesired classes from the class roots. + typedef std::set<std::string>::const_iterator ClassIt; // TODO: C++0x auto + for (ClassIt it = non_image_classes.begin(), end = non_image_classes.end(); it != end; ++it) { + class_linker->RemoveClass((*it).c_str(), NULL); + } + + // Clear references to removed classes from the DexCaches. + AbstractMethod* resolution_method = runtime->GetResolutionMethod(); + typedef Set::const_iterator CacheIt; // TODO: C++0x auto + for (CacheIt it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ++it) { + DexCache* dex_cache = *it; + for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { + Class* klass = dex_cache->GetResolvedType(i); + if (klass != NULL && !IsImageClass(klass)) { + dex_cache->SetResolvedType(i, NULL); + dex_cache->GetInitializedStaticStorage()->Set(i, NULL); + } + } + for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { + AbstractMethod* method = dex_cache->GetResolvedMethod(i); + if (method != NULL && !IsImageClass(method->GetDeclaringClass())) { + dex_cache->SetResolvedMethod(i, resolution_method); + } + } + for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { + Field* field = dex_cache->GetResolvedField(i); + if (field != NULL && !IsImageClass(field->GetDeclaringClass())) { + dex_cache->SetResolvedField(i, NULL); + } + } + } +} + +bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { + NonImageClasses* context = reinterpret_cast<NonImageClasses*>(arg); + if (!context->image_writer->IsImageClass(klass)) { + context->non_image_classes->insert(ClassHelper(klass).GetDescriptor()); + } + return true; +} + +void ImageWriter::CheckNonImageClassesRemoved() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (compiler_driver_.GetImageClasses() == NULL) { + return; + } + + gc::Heap* heap = Runtime::Current()->GetHeap(); + Thread* self = Thread::Current(); + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + } + + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->GetLiveBitmap()->Walk(CheckNonImageClassesRemovedCallback, this); +} + +void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { + ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); + if (!obj->IsClass()) { + return; + } + Class* klass = obj->AsClass(); + if (!image_writer->IsImageClass(klass)) { + image_writer->DumpImageClasses(); + CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() + << " " << PrettyDescriptor(klass); + } +} + +void ImageWriter::DumpImageClasses() { + CompilerDriver::DescriptorSet* image_classes = compiler_driver_.GetImageClasses(); + CHECK(image_classes != NULL); + typedef std::set<std::string>::const_iterator It; // TODO: C++0x auto + for (It it = image_classes->begin(), end = image_classes->end(); it != end; ++it) { + LOG(INFO) << " " << *it; + } +} + +void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) { + DCHECK(obj != NULL); + DCHECK(arg != NULL); + ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); + + // if it is a string, we want to intern it if its not interned. + if (obj->GetClass()->IsStringClass()) { + // we must be an interned string that was forward referenced and already assigned + if (image_writer->IsImageOffsetAssigned(obj)) { + DCHECK_EQ(obj, obj->AsString()->Intern()); + return; + } + SirtRef<String> interned(Thread::Current(), obj->AsString()->Intern()); + if (obj != interned.get()) { + if (!image_writer->IsImageOffsetAssigned(interned.get())) { + // interned obj is after us, allocate its location early + image_writer->AssignImageOffset(interned.get()); + } + // point those looking for this object to the interned version. + image_writer->SetImageOffset(obj, image_writer->GetImageOffset(interned.get())); + return; + } + // else (obj == interned), nothing to do but fall through to the normal case + } + + image_writer->AssignImageOffset(obj); +} + +ObjectArray<Object>* ImageWriter::CreateImageRoots() const { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;"); + Thread* self = Thread::Current(); + + // build an Object[] of all the DexCaches used in the source_space_ + ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class, + dex_caches_.size()); + int i = 0; + typedef Set::const_iterator It; // TODO: C++0x auto + for (It it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ++it, ++i) { + dex_caches->Set(i, *it); + } + + // build an Object[] of the roots needed to restore the runtime + SirtRef<ObjectArray<Object> > + image_roots(self, + ObjectArray<Object>::Alloc(self, object_array_class, + ImageHeader::kImageRootsMax)); + image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); + image_roots->Set(ImageHeader::kCalleeSaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kSaveAll)); + image_roots->Set(ImageHeader::kRefsOnlySaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)); + image_roots->Set(ImageHeader::kRefsAndArgsSaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); + image_roots->Set(ImageHeader::kOatLocation, + String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); + image_roots->Set(ImageHeader::kDexCaches, + dex_caches); + image_roots->Set(ImageHeader::kClassRoots, + class_linker->GetClassRoots()); + for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { + CHECK(image_roots->Get(i) != NULL); + } + return image_roots.get(); +} + +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()); + + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + DCHECK(!spaces.empty()); + DCHECK_EQ(0U, image_end_); + + // leave space for the header, but do not write it yet, we need to + // know where image_roots is going to end up + image_end_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment + + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + // TODO: Image spaces only? + // TODO: Add InOrderWalk to heap bitmap. + const char* old = self->StartAssertNoThreadSuspension("ImageWriter"); + DCHECK(heap->GetLargeObjectsSpace()->GetLiveObjects()->IsEmpty()); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + gc::space::ContinuousSpace* space = *it; + space->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); + DCHECK_LT(image_end_, image_->Size()); + } + self->EndAssertNoThreadSuspension(old); + } + + 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_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() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); + gc::Heap* heap = Runtime::Current()->GetHeap(); + // TODO: heap validation can't handle this fix up pass + heap->DisableObjectValidation(); + // TODO: Image spaces only? + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + heap->GetLiveBitmap()->Walk(CopyAndFixupObjectsCallback, this); + self->EndAssertNoThreadSuspension(old_cause); +} + +void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { + DCHECK(object != NULL); + DCHECK(arg != NULL); + const Object* obj = object; + ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); + + // see GetLocalAddress for similar computation + size_t offset = image_writer->GetImageOffset(obj); + byte* dst = image_writer->image_->Begin() + offset; + const byte* src = reinterpret_cast<const byte*>(obj); + size_t n = obj->SizeOf(); + DCHECK_LT(offset + n, image_writer->image_->Size()); + memcpy(dst, src, n); + Object* copy = reinterpret_cast<Object*>(dst); + copy->SetField32(Object::MonitorOffset(), 0, false); // We may have inflated the lock during compilation. + image_writer->FixupObject(obj, copy); +} + +void ImageWriter::FixupObject(const Object* orig, Object* copy) { + DCHECK(orig != NULL); + DCHECK(copy != NULL); + copy->SetClass(down_cast<Class*>(GetImageAddress(orig->GetClass()))); + // TODO: special case init of pointers to malloc data (or removal of these pointers) + if (orig->IsClass()) { + FixupClass(orig->AsClass(), down_cast<Class*>(copy)); + } else if (orig->IsObjectArray()) { + FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy)); + } else if (orig->IsMethod()) { + FixupMethod(orig->AsMethod(), down_cast<AbstractMethod*>(copy)); + } else { + FixupInstanceFields(orig, copy); + } +} + +void ImageWriter::FixupClass(const Class* orig, Class* copy) { + FixupInstanceFields(orig, copy); + FixupStaticFields(orig, copy); +} + +void ImageWriter::FixupMethod(const AbstractMethod* orig, AbstractMethod* copy) { + FixupInstanceFields(orig, copy); + + // OatWriter replaces the code_ with an offset value. + // Here we readjust to a pointer relative to oat_begin_ + if (orig->IsAbstract()) { + // Code for abstract methods is set to the abstract method error stub when we load the image. + copy->SetEntryPointFromCompiledCode(NULL); + copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*> + (GetOatAddress(interpreter_to_interpreter_entry_offset_))); + return; + } else { + copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*> + (GetOatAddress(interpreter_to_quick_entry_offset_))); + } + + if (orig == Runtime::Current()->GetResolutionMethod()) { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + return; + } + + // Use original code if it exists. Otherwise, set the code pointer to the resolution trampoline. + const byte* code = GetOatAddress(orig->GetOatCodeOffset()); + if (code != NULL) { + copy->SetEntryPointFromCompiledCode(code); + } else { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + } + + if (orig->IsNative()) { + // The native method's pointer is set to a stub to lookup via dlsym when we load the image. + // Note this is not the code_ pointer, that is handled above. + copy->SetNativeMethod(NULL); + } else { + // normal (non-abstract non-native) methods have mapping tables to relocate + uint32_t mapping_table_off = orig->GetOatMappingTableOffset(); + const byte* mapping_table = GetOatAddress(mapping_table_off); + copy->SetMappingTable(reinterpret_cast<const uint32_t*>(mapping_table)); + + uint32_t vmap_table_offset = orig->GetOatVmapTableOffset(); + const byte* vmap_table = GetOatAddress(vmap_table_offset); + copy->SetVmapTable(reinterpret_cast<const uint16_t*>(vmap_table)); + + uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset(); + const byte* native_gc_map = GetOatAddress(native_gc_map_offset); + copy->SetNativeGcMap(reinterpret_cast<const uint8_t*>(native_gc_map)); + } +} + +void ImageWriter::FixupObjectArray(const ObjectArray<Object>* orig, ObjectArray<Object>* copy) { + for (int32_t i = 0; i < orig->GetLength(); ++i) { + const Object* element = orig->Get(i); + copy->SetPtrWithoutChecks(i, GetImageAddress(element)); + } +} + +void ImageWriter::FixupInstanceFields(const Object* orig, Object* copy) { + DCHECK(orig != NULL); + DCHECK(copy != NULL); + Class* klass = orig->GetClass(); + DCHECK(klass != NULL); + FixupFields(orig, + copy, + klass->GetReferenceInstanceOffsets(), + false); +} + +void ImageWriter::FixupStaticFields(const Class* orig, Class* copy) { + DCHECK(orig != NULL); + DCHECK(copy != NULL); + FixupFields(orig, + copy, + orig->GetReferenceStaticOffsets(), + true); +} + +void ImageWriter::FixupFields(const Object* orig, + Object* copy, + uint32_t ref_offsets, + bool is_static) { + if (ref_offsets != CLASS_WALK_SUPER) { + // Found a reference offset bitmap. Fixup the specified offsets. + while (ref_offsets != 0) { + size_t right_shift = CLZ(ref_offsets); + MemberOffset byte_offset = CLASS_OFFSET_FROM_CLZ(right_shift); + const Object* ref = orig->GetFieldObject<const Object*>(byte_offset, false); + // Use SetFieldPtr to avoid card marking since we are writing to the image. + copy->SetFieldPtr(byte_offset, GetImageAddress(ref), false); + ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift); + } + } else { + // There is no reference offset bitmap. In the non-static case, + // walk up the class inheritance hierarchy and find reference + // offsets the hard way. In the static case, just consider this + // class. + for (const Class *klass = is_static ? orig->AsClass() : orig->GetClass(); + klass != NULL; + klass = is_static ? NULL : klass->GetSuperClass()) { + size_t num_reference_fields = (is_static + ? klass->NumReferenceStaticFields() + : klass->NumReferenceInstanceFields()); + for (size_t i = 0; i < num_reference_fields; ++i) { + Field* field = (is_static + ? klass->GetStaticField(i) + : klass->GetInstanceField(i)); + MemberOffset field_offset = field->GetOffset(); + const Object* ref = orig->GetFieldObject<const Object*>(field_offset, false); + // Use SetFieldPtr to avoid card marking since we are writing to the image. + copy->SetFieldPtr(field_offset, GetImageAddress(ref), false); + } + } + } + if (!is_static && orig->IsReferenceInstance()) { + // Fix-up referent, that isn't marked as an object field, for References. + Field* field = orig->GetClass()->FindInstanceField("referent", "Ljava/lang/Object;"); + MemberOffset field_offset = field->GetOffset(); + const Object* ref = orig->GetFieldObject<const Object*>(field_offset, false); + // Use SetFieldPtr to avoid card marking since we are writing to the image. + copy->SetFieldPtr(field_offset, GetImageAddress(ref), false); + } +} + +static AbstractMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + DexCache* dex_cache = class_linker->FindDexCache(patch->GetDexFile()); + AbstractMethod* method = class_linker->ResolveMethod(patch->GetDexFile(), + patch->GetTargetMethodIdx(), + dex_cache, + NULL, + NULL, + patch->GetTargetInvokeType()); + CHECK(method != NULL) + << patch->GetDexFile().GetLocation() << " " << patch->GetTargetMethodIdx(); + CHECK(!method->IsRuntimeMethod()) + << patch->GetDexFile().GetLocation() << " " << patch->GetTargetMethodIdx(); + CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method) + << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " " + << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " " + << PrettyMethod(method); + return method; +} + +void ImageWriter::PatchOatCodeAndMethods() { + Thread* self = Thread::Current(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); + + typedef std::vector<const CompilerDriver::PatchInformation*> Patches; + const Patches& code_to_patch = compiler_driver_.GetCodeToPatch(); + for (size_t i = 0; i < code_to_patch.size(); i++) { + const CompilerDriver::PatchInformation* patch = code_to_patch[i]; + AbstractMethod* target = GetTargetMethod(patch); + uint32_t code = reinterpret_cast<uint32_t>(class_linker->GetOatCodeFor(target)); + uint32_t code_base = reinterpret_cast<uint32_t>(&oat_file_->GetOatHeader()); + uint32_t code_offset = code - code_base; + SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetOatAddress(code_offset))); + } + + const Patches& methods_to_patch = compiler_driver_.GetMethodsToPatch(); + for (size_t i = 0; i < methods_to_patch.size(); i++) { + const CompilerDriver::PatchInformation* patch = methods_to_patch[i]; + AbstractMethod* target = GetTargetMethod(patch); + SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetImageAddress(target))); + } + + // Update the image header with the new checksum after patching + ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); + image_header->SetOatChecksum(oat_file_->GetOatHeader().GetChecksum()); + self->EndAssertNoThreadSuspension(old_cause); +} + +void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const void* oat_code = class_linker->GetOatCodeFor(patch->GetDexFile(), + patch->GetReferrerMethodIdx()); + OatHeader& oat_header = const_cast<OatHeader&>(oat_file_->GetOatHeader()); + // TODO: make this Thumb2 specific + uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uint32_t>(oat_code) & ~0x1); + uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset()); +#ifndef NDEBUG + const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); + uint32_t expected = reinterpret_cast<uint32_t>(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; +#endif + *patch_location = value; + oat_header.UpdateChecksum(patch_location, sizeof(value)); +} + +} // namespace art diff --git a/src/compiler/image_writer.h b/src/compiler/image_writer.h new file mode 100644 index 0000000000..b79cb2f0c2 --- /dev/null +++ b/src/compiler/image_writer.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_IMAGE_WRITER_H_ +#define ART_SRC_IMAGE_WRITER_H_ + +#include <stdint.h> + +#include <cstddef> +#include <set> +#include <string> + +#include "compiler/driver/compiler_driver.h" +#include "mem_map.h" +#include "oat_file.h" +#include "mirror/dex_cache.h" +#include "os.h" +#include "safe_map.h" +#include "gc/space/space.h" +#include "UniquePtr.h" + +namespace art { + +// Write a Space built during compilation for use during execution. +class ImageWriter { + public: + explicit ImageWriter(const CompilerDriver& compiler_driver) + : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL), + oat_data_begin_(NULL), interpreter_to_interpreter_entry_offset_(0), + interpreter_to_quick_entry_offset_(0), portable_resolution_trampoline_offset_(0), + quick_resolution_trampoline_offset_(0) {} + + ~ImageWriter() {} + + bool Write(const std::string& image_filename, + uintptr_t image_begin, + const std::string& oat_filename, + const std::string& oat_location) + LOCKS_EXCLUDED(Locks::mutator_lock_); + + uintptr_t GetOatDataBegin() { + return reinterpret_cast<uintptr_t>(oat_data_begin_); + } + + private: + bool AllocMemory(); + + // we use the lock word to store the offset of the object in the image + void AssignImageOffset(mirror::Object* object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(object != NULL); + SetImageOffset(object, image_end_); + image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment + DCHECK_LT(image_end_, image_->Size()); + } + + void SetImageOffset(mirror::Object* object, size_t offset) { + DCHECK(object != NULL); + DCHECK_NE(offset, 0U); + DCHECK(!IsImageOffsetAssigned(object)); + offsets_.Put(object, offset); + } + + size_t IsImageOffsetAssigned(const mirror::Object* object) const { + DCHECK(object != NULL); + return offsets_.find(object) != offsets_.end(); + } + + size_t GetImageOffset(const mirror::Object* object) const { + DCHECK(object != NULL); + DCHECK(IsImageOffsetAssigned(object)); + return offsets_.find(object)->second; + } + + mirror::Object* GetImageAddress(const mirror::Object* object) const { + if (object == NULL) { + return NULL; + } + return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object)); + } + + mirror::Object* GetLocalAddress(const mirror::Object* object) const { + size_t offset = GetImageOffset(object); + byte* dst = image_->Begin() + offset; + return reinterpret_cast<mirror::Object*>(dst); + } + + const byte* GetOatAddress(uint32_t offset) const { +#if !defined(ART_USE_PORTABLE_COMPILER) + // With Quick, code is within the OatFile, as there are all in one + // .o ELF object. However with Portable, the code is always in + // different .o ELF objects. + DCHECK_LT(offset, oat_file_->Size()); +#endif + if (offset == 0) { + return NULL; + } + return oat_data_begin_ + offset; + } + + // Returns true if the class was in the original requested image classes list. + bool IsImageClass(const mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Debug aid that list of requested image classes. + void DumpImageClasses(); + + // Preinitializes some otherwise lazy fields (such as Class name) to avoid runtime image dirtying. + void ComputeLazyFieldsForImageClasses() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static bool ComputeLazyFieldsForClassesVisitor(mirror::Class* klass, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Wire dex cache resolved strings to strings in the image to avoid runtime resolution. + void ComputeEagerResolvedStrings(); + static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Remove unwanted classes from various roots. + void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static bool NonImageClassesVisitor(mirror::Class* c, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Verify unwanted classes removed. + void CheckNonImageClassesRemoved(); + static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Lays out where the image objects will be at runtime. + void CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* CreateImageRoots() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void CalculateNewObjectOffsetsCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Creates the contiguous image in memory and adjusts pointers. + void CopyAndFixupObjects(); + static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupClass(const mirror::Class* orig, mirror::Class* copy) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupMethod(const mirror::AbstractMethod* orig, mirror::AbstractMethod* copy) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupObject(const mirror::Object* orig, mirror::Object* copy) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupObjectArray(const mirror::ObjectArray<mirror::Object>* orig, + mirror::ObjectArray<mirror::Object>* copy) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupInstanceFields(const mirror::Object* orig, mirror::Object* copy) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupStaticFields(const mirror::Class* orig, mirror::Class* copy) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FixupFields(const mirror::Object* orig, mirror::Object* copy, uint32_t ref_offsets, + bool is_static) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Patches references in OatFile to expect runtime addresses. + void PatchOatCodeAndMethods() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + + const CompilerDriver& compiler_driver_; + + // Map of Object to where it will be at runtime. + SafeMap<const mirror::Object*, size_t> offsets_; + + // oat file with code for this image + OatFile* oat_file_; + + // Memory mapped for generating the image. + UniquePtr<MemMap> image_; + + // Offset to the free space in image_. + size_t image_end_; + + // Beginning target image address for the output image. + byte* image_begin_; + + // Beginning target oat address for the pointers from the output image to its oat file. + const byte* oat_data_begin_; + + // Offset from oat_data_begin_ to the stubs. + uint32_t interpreter_to_interpreter_entry_offset_; + uint32_t interpreter_to_quick_entry_offset_; + uint32_t portable_resolution_trampoline_offset_; + uint32_t quick_resolution_trampoline_offset_; + + // DexCaches seen while scanning for fixing up CodeAndDirectMethods + typedef std::set<mirror::DexCache*> Set; + Set dex_caches_; +}; + +} // namespace art + +#endif // ART_SRC_IMAGE_WRITER_H_ diff --git a/src/compiler/jni/portable/jni_compiler.cc b/src/compiler/jni/portable/jni_compiler.cc index 2795d05f4e..cd8158d0ed 100644 --- a/src/compiler/jni/portable/jni_compiler.cc +++ b/src/compiler/jni/portable/jni_compiler.cc @@ -24,7 +24,7 @@ #include "compiler/llvm/compiler_llvm.h" #include "compiler/llvm/ir_builder.h" #include "compiler/llvm/llvm_compilation_unit.h" -#include "compiler/llvm/runtime_support_func.h" +#include "compiler/llvm/runtime_support_llvm_func.h" #include "compiler/llvm/utils_llvm.h" #include "dex_file-inl.h" #include "mirror/abstract_method.h" diff --git a/src/compiler/llvm/compiler_llvm.cc b/src/compiler/llvm/compiler_llvm.cc index dfd0e326d2..3af6687f65 100644 --- a/src/compiler/llvm/compiler_llvm.cc +++ b/src/compiler/llvm/compiler_llvm.cc @@ -152,8 +152,8 @@ CompileDexMethod(DexCompilationUnit* dex_compilation_unit, InvokeType invoke_typ cunit->Materialize(); - CompilerDriver::MethodReference mref(dex_compilation_unit->GetDexFile(), - dex_compilation_unit->GetDexMethodIndex()); + MethodReference mref(dex_compilation_unit->GetDexFile(), + dex_compilation_unit->GetDexMethodIndex()); return new CompiledMethod(compiler_driver_->GetInstructionSet(), cunit->GetElfObject(), *verifier::MethodVerifier::GetDexGcMap(mref), diff --git a/src/compiler/llvm/gbc_expander.cc b/src/compiler/llvm/gbc_expander.cc index bdf9aca68f..4702b37de1 100644 --- a/src/compiler/llvm/gbc_expander.cc +++ b/src/compiler/llvm/gbc_expander.cc @@ -19,6 +19,7 @@ #include "dex_file-inl.h" #include "intrinsic_helper.h" #include "ir_builder.h" +#include "method_reference.h" #include "mirror/abstract_method.h" #include "mirror/array.h" #include "mirror/string.h" @@ -776,8 +777,8 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { art::InvokeType invoke_type = static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0))); bool is_static = (invoke_type == art::kStatic); - art::CompilerDriver::MethodReference target_method(dex_compilation_unit_->GetDexFile(), - LV2UInt(call_inst.getArgOperand(1))); + art::MethodReference target_method(dex_compilation_unit_->GetDexFile(), + LV2UInt(call_inst.getArgOperand(1))); // Load *this* actual parameter llvm::Value* this_addr = (!is_static) ? call_inst.getArgOperand(3) : NULL; diff --git a/src/compiler/llvm/ir_builder.h b/src/compiler/llvm/ir_builder.h index 9d78fe6eaa..eb88fcac18 100644 --- a/src/compiler/llvm/ir_builder.h +++ b/src/compiler/llvm/ir_builder.h @@ -22,7 +22,7 @@ #include "intrinsic_helper.h" #include "md_builder.h" #include "runtime_support_builder.h" -#include "runtime_support_func.h" +#include "runtime_support_llvm_func.h" #include <llvm/IR/Constants.h> #include <llvm/IR/DerivedTypes.h> diff --git a/src/compiler/llvm/llvm_compilation_unit.h b/src/compiler/llvm/llvm_compilation_unit.h index 857d924840..a5d4e117e6 100644 --- a/src/compiler/llvm/llvm_compilation_unit.h +++ b/src/compiler/llvm/llvm_compilation_unit.h @@ -25,7 +25,7 @@ #include "instruction_set.h" #include "compiler/driver/dex_compilation_unit.h" #include "runtime_support_builder.h" -#include "runtime_support_func.h" +#include "runtime_support_llvm_func.h" #include "safe_map.h" #include <UniquePtr.h> diff --git a/src/compiler/llvm/runtime_support_builder.cc b/src/compiler/llvm/runtime_support_builder.cc index 2be2ddf36f..28405f67d4 100644 --- a/src/compiler/llvm/runtime_support_builder.cc +++ b/src/compiler/llvm/runtime_support_builder.cc @@ -48,7 +48,7 @@ RuntimeSupportBuilder::RuntimeSupportBuilder(::llvm::LLVMContext& context, runtime_support_func_decls_[runtime_support::ID] = fn; \ } while (0); -#include "runtime_support_func_list.h" +#include "runtime_support_llvm_func_list.h" RUNTIME_SUPPORT_FUNC_LIST(GET_RUNTIME_SUPPORT_FUNC_DECL) #undef RUNTIME_SUPPORT_FUNC_LIST #undef GET_RUNTIME_SUPPORT_FUNC_DECL diff --git a/src/compiler/llvm/runtime_support_builder.h b/src/compiler/llvm/runtime_support_builder.h index 04d72b85b0..267b406232 100644 --- a/src/compiler/llvm/runtime_support_builder.h +++ b/src/compiler/llvm/runtime_support_builder.h @@ -19,7 +19,7 @@ #include "backend_types.h" #include "base/logging.h" -#include "runtime_support_func.h" +#include "runtime_support_llvm_func.h" #include <stdint.h> diff --git a/src/compiler/llvm/runtime_support_func_list.h b/src/compiler/llvm/runtime_support_func_list.h deleted file mode 100644 index a58b061e16..0000000000 --- a/src/compiler/llvm/runtime_support_func_list.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define RUNTIME_SUPPORT_FUNC_LIST(V) \ - V(LockObject, art_portable_lock_object_from_code) \ - V(UnlockObject, art_portable_unlock_object_from_code) \ - V(GetCurrentThread, art_portable_get_current_thread_from_code) \ - V(SetCurrentThread, art_portable_set_current_thread_from_code) \ - V(PushShadowFrame, art_portable_push_shadow_frame_from_code) \ - V(PopShadowFrame, art_portable_pop_shadow_frame_from_code) \ - V(TestSuspend, art_portable_test_suspend_from_code) \ - V(ThrowException, art_portable_throw_exception_from_code) \ - V(ThrowStackOverflowException, art_portable_throw_stack_overflow_from_code) \ - V(ThrowNullPointerException, art_portable_throw_null_pointer_exception_from_code) \ - V(ThrowDivZeroException, art_portable_throw_div_zero_from_code) \ - V(ThrowIndexOutOfBounds, art_portable_throw_array_bounds_from_code) \ - V(InitializeTypeAndVerifyAccess, art_portable_initialize_type_and_verify_access_from_code) \ - V(InitializeType, art_portable_initialize_type_from_code) \ - V(IsAssignable, art_portable_is_assignable_from_code) \ - V(CheckCast, art_portable_check_cast_from_code) \ - V(CheckPutArrayElement, art_portable_check_put_array_element_from_code) \ - V(AllocObject, art_portable_alloc_object_from_code) \ - V(AllocObjectWithAccessCheck, art_portable_alloc_object_from_code_with_access_check) \ - V(AllocArray, art_portable_alloc_array_from_code) \ - V(AllocArrayWithAccessCheck, art_portable_alloc_array_from_code_with_access_check) \ - V(CheckAndAllocArray, art_portable_check_and_alloc_array_from_code) \ - V(CheckAndAllocArrayWithAccessCheck, art_portable_check_and_alloc_array_from_code_with_access_check) \ - V(FindStaticMethodWithAccessCheck, art_portable_find_static_method_from_code_with_access_check) \ - V(FindDirectMethodWithAccessCheck, art_portable_find_direct_method_from_code_with_access_check) \ - V(FindVirtualMethodWithAccessCheck, art_portable_find_virtual_method_from_code_with_access_check) \ - V(FindSuperMethodWithAccessCheck, art_portable_find_super_method_from_code_with_access_check) \ - V(FindInterfaceMethodWithAccessCheck, art_portable_find_interface_method_from_code_with_access_check) \ - V(FindInterfaceMethod, art_portable_find_interface_method_from_code) \ - V(ResolveString, art_portable_resolve_string_from_code) \ - V(Set32Static, art_portable_set32_static_from_code) \ - V(Set64Static, art_portable_set64_static_from_code) \ - V(SetObjectStatic, art_portable_set_obj_static_from_code) \ - V(Get32Static, art_portable_get32_static_from_code) \ - V(Get64Static, art_portable_get64_static_from_code) \ - V(GetObjectStatic, art_portable_get_obj_static_from_code) \ - V(Set32Instance, art_portable_set32_instance_from_code) \ - V(Set64Instance, art_portable_set64_instance_from_code) \ - V(SetObjectInstance, art_portable_set_obj_instance_from_code) \ - V(Get32Instance, art_portable_get32_instance_from_code) \ - V(Get64Instance, art_portable_get64_instance_from_code) \ - V(GetObjectInstance, art_portable_get_obj_instance_from_code) \ - V(InitializeStaticStorage, art_portable_initialize_static_storage_from_code) \ - V(FillArrayData, art_portable_fill_array_data_from_code) \ - V(GetAndClearException, art_portable_get_and_clear_exception) \ - V(IsExceptionPending, art_portable_is_exception_pending_from_code) \ - V(FindCatchBlock, art_portable_find_catch_block_from_code) \ - V(MarkGCCard, art_portable_mark_gc_card_from_code) \ - V(ProxyInvokeHandler, art_portable_proxy_invoke_handler_from_code) \ - V(art_d2l, art_d2l) \ - V(art_d2i, art_d2i) \ - V(art_f2l, art_f2l) \ - V(art_f2i, art_f2i) \ - V(JniMethodStart, art_portable_jni_method_start) \ - V(JniMethodStartSynchronized, art_portable_jni_method_start_synchronized) \ - V(JniMethodEnd, art_portable_jni_method_end) \ - V(JniMethodEndSynchronized, art_portable_jni_method_end_synchronized) \ - V(JniMethodEndWithReference, art_portable_jni_method_end_with_reference) \ - V(JniMethodEndWithReferenceSynchronized, art_portable_jni_method_end_with_reference_synchronized) diff --git a/src/compiler/llvm/runtime_support_llvm.cc b/src/compiler/llvm/runtime_support_llvm.cc deleted file mode 100644 index bff13f9dc9..0000000000 --- a/src/compiler/llvm/runtime_support_llvm.cc +++ /dev/null @@ -1,849 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "runtime_support_llvm.h" - -#include "ScopedLocalRef.h" -#include "asm_support.h" -#include "class_linker.h" -#include "class_linker-inl.h" -#include "dex_file-inl.h" -#include "dex_instruction.h" -#include "mirror/abstract_method-inl.h" -#include "mirror/class-inl.h" -#include "mirror/dex_cache-inl.h" -#include "mirror/field-inl.h" -#include "mirror/object.h" -#include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" -#include "nth_caller_visitor.h" -#include "object_utils.h" -#include "reflection.h" -#include "runtime_support.h" -#include "runtime_support_func_list.h" -#include "scoped_thread_state_change.h" -#include "thread.h" -#include "thread_list.h" -#include "utils_llvm.h" -#include "verifier/dex_gc_map.h" -#include "verifier/method_verifier.h" -#include "well_known_classes.h" - -#include <algorithm> -#include <math.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdlib.h> - -using namespace art; - -extern "C" { - -class ShadowFrameCopyVisitor : public StackVisitor { - public: - explicit ShadowFrameCopyVisitor(Thread* self) : StackVisitor(self, NULL), prev_frame_(NULL), - top_frame_(NULL) {} - - bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (IsShadowFrame()) { - ShadowFrame* cur_frame = GetCurrentShadowFrame(); - size_t num_regs = cur_frame->NumberOfVRegs(); - mirror::AbstractMethod* method = cur_frame->GetMethod(); - uint32_t dex_pc = cur_frame->GetDexPC(); - ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, method, dex_pc); - - const uint8_t* gc_map = method->GetNativeGcMap(); - uint32_t gc_map_length = static_cast<uint32_t>((gc_map[0] << 24) | - (gc_map[1] << 16) | - (gc_map[2] << 8) | - (gc_map[3] << 0)); - verifier::DexPcToReferenceMap dex_gc_map(gc_map + 4, gc_map_length); - const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc); - for (size_t reg = 0; reg < num_regs; ++reg) { - if (TestBitmap(reg, reg_bitmap)) { - new_frame->SetVRegReference(reg, cur_frame->GetVRegReference(reg)); - } else { - new_frame->SetVReg(reg, cur_frame->GetVReg(reg)); - } - } - - if (prev_frame_ != NULL) { - prev_frame_->SetLink(new_frame); - } else { - top_frame_ = new_frame; - } - prev_frame_ = new_frame; - } - return true; - } - - ShadowFrame* GetShadowFrameCopy() { - return top_frame_; - } - - private: - static bool TestBitmap(int reg, const uint8_t* reg_vector) { - return ((reg_vector[reg / 8] >> (reg % 8)) & 0x01) != 0; - } - - ShadowFrame* prev_frame_; - ShadowFrame* top_frame_; -}; - -//---------------------------------------------------------------------------- -// Thread -//---------------------------------------------------------------------------- - -Thread* art_portable_get_current_thread_from_code() { -#if defined(__arm__) || defined(__i386__) - LOG(FATAL) << "UNREACHABLE"; -#endif - return Thread::Current(); -} - -void* art_portable_set_current_thread_from_code(void* thread_object_addr) { - // Hijacked to set r9 on ARM. - LOG(FATAL) << "UNREACHABLE"; - return NULL; -} - -void art_portable_lock_object_from_code(mirror::Object* obj, Thread* thread) - EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) { - DCHECK(obj != NULL); // Assumed to have been checked before entry - obj->MonitorEnter(thread); // May block - DCHECK(thread->HoldsLock(obj)); - // Only possible exception is NPE and is handled before entry - DCHECK(!thread->IsExceptionPending()); -} - -void art_portable_unlock_object_from_code(mirror::Object* obj, Thread* thread) - UNLOCK_FUNCTION(monitor_lock_) { - DCHECK(obj != NULL); // Assumed to have been checked before entry - // MonitorExit may throw exception - obj->MonitorExit(thread); -} - -void art_portable_test_suspend_from_code(Thread* self) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CheckSuspend(self); - if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) { - // Save out the shadow frame to the heap - ShadowFrameCopyVisitor visitor(self); - visitor.WalkStack(true); - self->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy()); - self->SetDeoptimizationReturnValue(JValue()); - self->SetException(ThrowLocation(), reinterpret_cast<mirror::Throwable*>(-1)); - } -} - -ShadowFrame* art_portable_push_shadow_frame_from_code(Thread* thread, - ShadowFrame* new_shadow_frame, - mirror::AbstractMethod* method, - uint32_t num_vregs) { - ShadowFrame* old_frame = thread->PushShadowFrame(new_shadow_frame); - new_shadow_frame->SetMethod(method); - new_shadow_frame->SetNumberOfVRegs(num_vregs); - return old_frame; -} - -void art_portable_pop_shadow_frame_from_code(void*) { - LOG(FATAL) << "Implemented by IRBuilder."; -} - -void art_portable_mark_gc_card_from_code(void *, void*) { - LOG(FATAL) << "Implemented by IRBuilder."; -} - -//---------------------------------------------------------------------------- -// Exception -//---------------------------------------------------------------------------- - -bool art_portable_is_exception_pending_from_code() { - LOG(FATAL) << "Implemented by IRBuilder."; - return false; -} - -void art_portable_throw_div_zero_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ThrowArithmeticExceptionDivideByZero(); -} - -void art_portable_throw_array_bounds_from_code(int32_t index, int32_t length) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ThrowArrayIndexOutOfBoundsException(index, length); -} - -void art_portable_throw_no_such_method_from_code(int32_t method_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ThrowNoSuchMethodError(method_idx); -} - -void art_portable_throw_null_pointer_exception_from_code(uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO: remove dex_pc argument from caller. - UNUSED(dex_pc); - Thread* self = Thread::Current(); - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - ThrowNullPointerExceptionFromDexPC(throw_location); -} - -void art_portable_throw_stack_overflow_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ThrowStackOverflowError(Thread::Current()); -} - -void art_portable_throw_exception_from_code(mirror::Throwable* exception) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Thread* self = Thread::Current(); - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - if (exception == NULL) { - ThrowNullPointerException(NULL, "throw with null exception"); - } else { - self->SetException(throw_location, exception); - } -} - -void* art_portable_get_and_clear_exception(Thread* self) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(self->IsExceptionPending()); - // TODO: make this inline. - mirror::Throwable* exception = self->GetException(NULL); - self->ClearException(); - return exception; -} - -int32_t art_portable_find_catch_block_from_code(mirror::AbstractMethod* current_method, uint32_t ti_offset) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Thread* self = Thread::Current(); // TODO: make an argument. - ThrowLocation throw_location; - mirror::Throwable* exception = self->GetException(&throw_location); - // Check for special deoptimization exception. - if (UNLIKELY(reinterpret_cast<int32_t>(exception) == -1)) { - return -1; - } - mirror::Class* exception_type = exception->GetClass(); - MethodHelper mh(current_method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - DCHECK_LT(ti_offset, code_item->tries_size_); - const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset); - - int iter_index = 0; - int result = -1; - uint32_t catch_dex_pc = -1; - // Iterate over the catch handlers associated with dex_pc - for (CatchHandlerIterator it(*code_item, *try_item); it.HasNext(); it.Next()) { - uint16_t iter_type_idx = it.GetHandlerTypeIndex(); - // Catch all case - if (iter_type_idx == DexFile::kDexNoIndex16) { - catch_dex_pc = it.GetHandlerAddress(); - result = iter_index; - break; - } - // Does this catch exception type apply? - mirror::Class* iter_exception_type = mh.GetDexCacheResolvedType(iter_type_idx); - if (UNLIKELY(iter_exception_type == NULL)) { - // TODO: check, the verifier (class linker?) should take care of resolving all exception - // classes early. - LOG(WARNING) << "Unresolved exception class when finding catch block: " - << mh.GetTypeDescriptorFromTypeIdx(iter_type_idx); - } else if (iter_exception_type->IsAssignableFrom(exception_type)) { - catch_dex_pc = it.GetHandlerAddress(); - result = iter_index; - break; - } - ++iter_index; - } - if (result != -1) { - // Handler found. - Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(self, - throw_location, - current_method, - catch_dex_pc, - exception); - } - return result; -} - - -//---------------------------------------------------------------------------- -// Object Space -//---------------------------------------------------------------------------- - -mirror::Object* art_portable_alloc_object_from_code(uint32_t type_idx, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectFromCode(type_idx, referrer, thread, false); -} - -mirror::Object* art_portable_alloc_object_from_code_with_access_check(uint32_t type_idx, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectFromCode(type_idx, referrer, thread, true); -} - -mirror::Object* art_portable_alloc_array_from_code(uint32_t type_idx, - mirror::AbstractMethod* referrer, - uint32_t length, - Thread* self) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocArrayFromCode(type_idx, referrer, length, self, false); -} - -mirror::Object* art_portable_alloc_array_from_code_with_access_check(uint32_t type_idx, - mirror::AbstractMethod* referrer, - uint32_t length, - Thread* self) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocArrayFromCode(type_idx, referrer, length, self, true); -} - -mirror::Object* art_portable_check_and_alloc_array_from_code(uint32_t type_idx, - mirror::AbstractMethod* referrer, - uint32_t length, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, false); -} - -mirror::Object* art_portable_check_and_alloc_array_from_code_with_access_check(uint32_t type_idx, - mirror::AbstractMethod* referrer, - uint32_t length, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, true); -} - -static mirror::AbstractMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object, - mirror::AbstractMethod* caller_method, bool access_check, - InvokeType type, Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::AbstractMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); - if (UNLIKELY(method == NULL)) { - method = FindMethodFromCode(method_idx, this_object, caller_method, - thread, access_check, type); - if (UNLIKELY(method == NULL)) { - CHECK(thread->IsExceptionPending()); - return 0; // failure - } - } - DCHECK(!thread->IsExceptionPending()); - const void* code = method->GetEntryPointFromCompiledCode(); - - // When we return, the caller will branch to this address, so it had better not be 0! - if (UNLIKELY(code == NULL)) { - MethodHelper mh(method); - LOG(FATAL) << "Code was NULL in method: " << PrettyMethod(method) - << " location: " << mh.GetDexFile().GetLocation(); - } - return method; -} - -mirror::Object* art_portable_find_static_method_from_code_with_access_check(uint32_t method_idx, - mirror::Object* this_object, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kStatic, thread); -} - -mirror::Object* art_portable_find_direct_method_from_code_with_access_check(uint32_t method_idx, - mirror::Object* this_object, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kDirect, thread); -} - -mirror::Object* art_portable_find_virtual_method_from_code_with_access_check(uint32_t method_idx, - mirror::Object* this_object, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kVirtual, thread); -} - -mirror::Object* art_portable_find_super_method_from_code_with_access_check(uint32_t method_idx, - mirror::Object* this_object, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kSuper, thread); -} - -mirror::Object* -art_portable_find_interface_method_from_code_with_access_check(uint32_t method_idx, - mirror::Object* this_object, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kInterface, thread); -} - -mirror::Object* art_portable_find_interface_method_from_code(uint32_t method_idx, - mirror::Object* this_object, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, false, kInterface, thread); -} - -mirror::Object* art_portable_initialize_static_storage_from_code(uint32_t type_idx, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return ResolveVerifyAndClinit(type_idx, referrer, thread, true, false); -} - -mirror::Object* art_portable_initialize_type_from_code(uint32_t type_idx, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return ResolveVerifyAndClinit(type_idx, referrer, thread, false, false); -} - -mirror::Object* art_portable_initialize_type_and_verify_access_from_code(uint32_t type_idx, - mirror::AbstractMethod* referrer, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Called when caller isn't guaranteed to have access to a type and the dex cache may be - // unpopulated - return ResolveVerifyAndClinit(type_idx, referrer, thread, false, true); -} - -mirror::Object* art_portable_resolve_string_from_code(mirror::AbstractMethod* referrer, uint32_t string_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return ResolveStringFromCode(referrer, string_idx); -} - -int32_t art_portable_set32_static_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, int32_t new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint32_t)); - if (LIKELY(field != NULL)) { - field->Set32(field->GetDeclaringClass(), new_value); - return 0; - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveWrite, sizeof(uint32_t), true); - if (LIKELY(field != NULL)) { - field->Set32(field->GetDeclaringClass(), new_value); - return 0; - } - return -1; -} - -int32_t art_portable_set64_static_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, int64_t new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t)); - if (LIKELY(field != NULL)) { - field->Set64(field->GetDeclaringClass(), new_value); - return 0; - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveWrite, sizeof(uint64_t), true); - if (LIKELY(field != NULL)) { - field->Set64(field->GetDeclaringClass(), new_value); - return 0; - } - return -1; -} - -int32_t art_portable_set_obj_static_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, mirror::Object* new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, StaticObjectWrite, sizeof(mirror::Object*)); - if (LIKELY(field != NULL)) { - field->SetObj(field->GetDeclaringClass(), new_value); - return 0; - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticObjectWrite, sizeof(mirror::Object*), true); - if (LIKELY(field != NULL)) { - field->SetObj(field->GetDeclaringClass(), new_value); - return 0; - } - return -1; -} - -int32_t art_portable_get32_static_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint32_t)); - if (LIKELY(field != NULL)) { - return field->Get32(field->GetDeclaringClass()); - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveRead, sizeof(uint32_t), true); - if (LIKELY(field != NULL)) { - return field->Get32(field->GetDeclaringClass()); - } - return 0; -} - -int64_t art_portable_get64_static_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint64_t)); - if (LIKELY(field != NULL)) { - return field->Get64(field->GetDeclaringClass()); - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveRead, sizeof(uint64_t), true); - if (LIKELY(field != NULL)) { - return field->Get64(field->GetDeclaringClass()); - } - return 0; -} - -mirror::Object* art_portable_get_obj_static_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, StaticObjectRead, sizeof(mirror::Object*)); - if (LIKELY(field != NULL)) { - return field->GetObj(field->GetDeclaringClass()); - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticObjectRead, sizeof(mirror::Object*), true); - if (LIKELY(field != NULL)) { - return field->GetObj(field->GetDeclaringClass()); - } - return 0; -} - -int32_t art_portable_set32_instance_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, - mirror::Object* obj, uint32_t new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t)); - if (LIKELY(field != NULL)) { - field->Set32(obj, new_value); - return 0; - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveWrite, sizeof(uint32_t), true); - if (LIKELY(field != NULL)) { - field->Set32(obj, new_value); - return 0; - } - return -1; -} - -int32_t art_portable_set64_instance_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, - mirror::Object* obj, int64_t new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t)); - if (LIKELY(field != NULL)) { - field->Set64(obj, new_value); - return 0; - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveWrite, sizeof(uint64_t), true); - if (LIKELY(field != NULL)) { - field->Set64(obj, new_value); - return 0; - } - return -1; -} - -int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, - mirror::Object* obj, mirror::Object* new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(mirror::Object*)); - if (LIKELY(field != NULL)) { - field->SetObj(obj, new_value); - return 0; - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstanceObjectWrite, sizeof(mirror::Object*), true); - if (LIKELY(field != NULL)) { - field->SetObj(obj, new_value); - return 0; - } - return -1; -} - -int32_t art_portable_get32_instance_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, mirror::Object* obj) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint32_t)); - if (LIKELY(field != NULL)) { - return field->Get32(obj); - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveRead, sizeof(uint32_t), true); - if (LIKELY(field != NULL)) { - return field->Get32(obj); - } - return 0; -} - -int64_t art_portable_get64_instance_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, mirror::Object* obj) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint64_t)); - if (LIKELY(field != NULL)) { - return field->Get64(obj); - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveRead, sizeof(uint64_t), true); - if (LIKELY(field != NULL)) { - return field->Get64(obj); - } - return 0; -} - -mirror::Object* art_portable_get_obj_instance_from_code(uint32_t field_idx, mirror::AbstractMethod* referrer, mirror::Object* obj) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Field* field = FindFieldFast(field_idx, referrer, InstanceObjectRead, sizeof(mirror::Object*)); - if (LIKELY(field != NULL)) { - return field->GetObj(obj); - } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstanceObjectRead, sizeof(mirror::Object*), true); - if (LIKELY(field != NULL)) { - return field->GetObj(obj); - } - return 0; -} - -void art_portable_fill_array_data_from_code(mirror::AbstractMethod* method, uint32_t dex_pc, - mirror::Array* array, uint32_t payload_offset) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Test: Is array equal to null? (Guard NullPointerException) - if (UNLIKELY(array == NULL)) { - art_portable_throw_null_pointer_exception_from_code(dex_pc); - return; - } - - // Find the payload from the CodeItem - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - - DCHECK_GT(code_item->insns_size_in_code_units_, payload_offset); - - const Instruction::ArrayDataPayload* payload = - reinterpret_cast<const Instruction::ArrayDataPayload*>( - code_item->insns_ + payload_offset); - - DCHECK_EQ(payload->ident, - static_cast<uint16_t>(Instruction::kArrayDataSignature)); - - // Test: Is array big enough? - uint32_t array_len = static_cast<uint32_t>(array->GetLength()); - if (UNLIKELY(array_len < payload->element_count)) { - int32_t last_index = payload->element_count - 1; - art_portable_throw_array_bounds_from_code(array_len, last_index); - return; - } - - // Copy the data - size_t size = payload->element_width * payload->element_count; - memcpy(array->GetRawData(payload->element_width), payload->data, size); -} - - - -//---------------------------------------------------------------------------- -// Type checking, in the nature of casting -//---------------------------------------------------------------------------- - -int32_t art_portable_is_assignable_from_code(const mirror::Class* dest_type, const mirror::Class* src_type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(dest_type != NULL); - DCHECK(src_type != NULL); - return dest_type->IsAssignableFrom(src_type) ? 1 : 0; -} - -void art_portable_check_cast_from_code(const mirror::Class* dest_type, const mirror::Class* src_type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(dest_type->IsClass()) << PrettyClass(dest_type); - DCHECK(src_type->IsClass()) << PrettyClass(src_type); - if (UNLIKELY(!dest_type->IsAssignableFrom(src_type))) { - ThrowClassCastException(dest_type, src_type); - } -} - -void art_portable_check_put_array_element_from_code(const mirror::Object* element, - const mirror::Object* array) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (element == NULL) { - return; - } - DCHECK(array != NULL); - mirror::Class* array_class = array->GetClass(); - DCHECK(array_class != NULL); - mirror::Class* component_type = array_class->GetComponentType(); - mirror::Class* element_class = element->GetClass(); - if (UNLIKELY(!component_type->IsAssignableFrom(element_class))) { - ThrowArrayStoreException(element_class, array_class); - } - return; -} - -//---------------------------------------------------------------------------- -// JNI -//---------------------------------------------------------------------------- - -// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_. -uint32_t art_portable_jni_method_start(Thread* self) - UNLOCK_FUNCTION(GlobalSynchronizatio::mutator_lock_) { - JNIEnvExt* env = self->GetJniEnv(); - uint32_t saved_local_ref_cookie = env->local_ref_cookie; - env->local_ref_cookie = env->locals.GetSegmentState(); - self->TransitionFromRunnableToSuspended(kNative); - return saved_local_ref_cookie; -} - -uint32_t art_portable_jni_method_start_synchronized(jobject to_lock, Thread* self) - UNLOCK_FUNCTION(Locks::mutator_lock_) { - self->DecodeJObject(to_lock)->MonitorEnter(self); - return art_portable_jni_method_start(self); -} - -static inline void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) { - JNIEnvExt* env = self->GetJniEnv(); - env->locals.SetSegmentState(env->local_ref_cookie); - env->local_ref_cookie = saved_local_ref_cookie; -} - -void art_portable_jni_method_end(uint32_t saved_local_ref_cookie, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { - self->TransitionFromSuspendedToRunnable(); - PopLocalReferences(saved_local_ref_cookie, self); -} - - -void art_portable_jni_method_end_synchronized(uint32_t saved_local_ref_cookie, - jobject locked, - Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { - self->TransitionFromSuspendedToRunnable(); - UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. - PopLocalReferences(saved_local_ref_cookie, self); -} - -mirror::Object* art_portable_jni_method_end_with_reference(jobject result, uint32_t saved_local_ref_cookie, - Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { - self->TransitionFromSuspendedToRunnable(); - mirror::Object* o = self->DecodeJObject(result); // Must decode before pop. - PopLocalReferences(saved_local_ref_cookie, self); - // Process result. - if (UNLIKELY(self->GetJniEnv()->check_jni)) { - if (self->IsExceptionPending()) { - return NULL; - } - CheckReferenceResult(o, self); - } - return o; -} - -mirror::Object* art_portable_jni_method_end_with_reference_synchronized(jobject result, - uint32_t saved_local_ref_cookie, - jobject locked, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { - self->TransitionFromSuspendedToRunnable(); - UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. - mirror::Object* o = self->DecodeJObject(result); - PopLocalReferences(saved_local_ref_cookie, self); - // Process result. - if (UNLIKELY(self->GetJniEnv()->check_jni)) { - if (self->IsExceptionPending()) { - return NULL; - } - CheckReferenceResult(o, self); - } - return o; -} - -// Handler for invocation on proxy methods. Create a boxed argument array and invoke the invocation -// handler which is a field within the proxy object receiver. The var args encode the arguments -// with the last argument being a pointer to a JValue to store the result in. -void art_portable_proxy_invoke_handler_from_code(mirror::AbstractMethod* proxy_method, ...) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - va_list ap; - va_start(ap, proxy_method); - - mirror::Object* receiver = va_arg(ap, mirror::Object*); - Thread* self = va_arg(ap, Thread*); - MethodHelper proxy_mh(proxy_method); - - // Ensure we don't get thread suspension until the object arguments are safely in jobjects. - const char* old_cause = - self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments"); - self->VerifyStack(); - - // Start new JNI local reference state. - JNIEnvExt* env = self->GetJniEnv(); - ScopedObjectAccessUnchecked soa(env); - ScopedJniEnvLocalRefState env_state(env); - - // Create local ref. copies of the receiver. - jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver); - - // Convert proxy method into expected interface method. - mirror::AbstractMethod* interface_method = proxy_method->FindOverriddenMethod(); - DCHECK(interface_method != NULL); - DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method); - jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method); - - // Record arguments and turn mirror::Object* arguments into jobject to survive GC. - std::vector<jvalue> args; - const size_t num_params = proxy_mh.NumArgs(); - for (size_t i = 1; i < num_params; ++i) { - jvalue val; - switch (proxy_mh.GetParamPrimitiveType(i)) { - case Primitive::kPrimNot: - val.l = soa.AddLocalReference<jobject>(va_arg(ap, mirror::Object*)); - break; - case Primitive::kPrimBoolean: // Fall-through. - case Primitive::kPrimByte: // Fall-through. - case Primitive::kPrimChar: // Fall-through. - case Primitive::kPrimShort: // Fall-through. - case Primitive::kPrimInt: // Fall-through. - val.i = va_arg(ap, jint); - break; - case Primitive::kPrimFloat: - // TODO: should this be jdouble? Floats aren't passed to var arg routines. - val.i = va_arg(ap, jint); - break; - case Primitive::kPrimDouble: - val.d = (va_arg(ap, jdouble)); - break; - case Primitive::kPrimLong: - val.j = (va_arg(ap, jlong)); - break; - case Primitive::kPrimVoid: - LOG(FATAL) << "UNREACHABLE"; - val.j = 0; - break; - } - args.push_back(val); - } - self->EndAssertNoThreadSuspension(old_cause); - JValue* result_location = NULL; - const char* shorty = proxy_mh.GetShorty(); - if (shorty[0] != 'V') { - result_location = va_arg(ap, JValue*); - } - va_end(ap); - JValue result = InvokeProxyInvocationHandler(soa, shorty, rcvr_jobj, interface_method_jobj, args); - if (result_location != NULL) { - *result_location = result; - } -} - -//---------------------------------------------------------------------------- -// Memory barrier -//---------------------------------------------------------------------------- - -void art_portable_constructor_barrier() { - LOG(FATAL) << "Implemented by IRBuilder."; -} - -} // extern "C" diff --git a/src/compiler/llvm/runtime_support_func.h b/src/compiler/llvm/runtime_support_llvm_func.h index 6dfa961242..ac6f3b869f 100644 --- a/src/compiler/llvm/runtime_support_func.h +++ b/src/compiler/llvm/runtime_support_llvm_func.h @@ -23,7 +23,7 @@ namespace runtime_support { enum RuntimeId { #define DEFINE_RUNTIME_SUPPORT_FUNC_ID(ID, NAME) ID, -#include "runtime_support_func_list.h" +#include "runtime_support_llvm_func_list.h" RUNTIME_SUPPORT_FUNC_LIST(DEFINE_RUNTIME_SUPPORT_FUNC_ID) #undef RUNTIME_SUPPORT_FUNC_LIST #undef DEFINE_RUNTIME_SUPPORT_FUNC_ID diff --git a/src/compiler/oat_writer.cc b/src/compiler/oat_writer.cc new file mode 100644 index 0000000000..0bfa4ec328 --- /dev/null +++ b/src/compiler/oat_writer.cc @@ -0,0 +1,906 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oat_writer.h" + +#include <zlib.h> + +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" +#include "class_linker.h" +#include "dex_file-inl.h" +#include "gc/space/space.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/array.h" +#include "mirror/class_loader.h" +#include "mirror/object-inl.h" +#include "os.h" +#include "output_stream.h" +#include "safe_map.h" +#include "scoped_thread_state_change.h" +#include "verifier/method_verifier.h" + +namespace art { + +bool OatWriter::Create(OutputStream& output_stream, + const std::vector<const DexFile*>& dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_begin, + const std::string& image_file_location, + const CompilerDriver& driver) { + OatWriter oat_writer(dex_files, + image_file_location_oat_checksum, + image_file_location_oat_begin, + image_file_location, + &driver); + return oat_writer.Write(output_stream); +} + +OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_begin, + const std::string& image_file_location, + const CompilerDriver* compiler) + : compiler_driver_(compiler), + dex_files_(&dex_files), + image_file_location_oat_checksum_(image_file_location_oat_checksum), + image_file_location_oat_begin_(image_file_location_oat_begin), + image_file_location_(image_file_location), + oat_header_(NULL), + size_dex_file_alignment_(0), + size_executable_offset_alignment_(0), + size_oat_header_(0), + size_oat_header_image_file_location_(0), + size_dex_file_(0), + size_interpreter_to_interpreter_entry_(0), + size_interpreter_to_quick_entry_(0), + size_portable_resolution_trampoline_(0), + size_quick_resolution_trampoline_(0), + size_stubs_alignment_(0), + size_code_size_(0), + size_code_(0), + size_code_alignment_(0), + size_mapping_table_(0), + size_vmap_table_(0), + size_gc_map_(0), + size_oat_dex_file_location_size_(0), + size_oat_dex_file_location_data_(0), + size_oat_dex_file_location_checksum_(0), + size_oat_dex_file_offset_(0), + size_oat_dex_file_methods_offsets_(0), + size_oat_class_status_(0), + size_oat_class_method_offsets_(0) { + + size_t offset = InitOatHeader(); + offset = InitOatDexFiles(offset); + offset = InitDexFiles(offset); + offset = InitOatClasses(offset); + offset = InitOatCode(offset); + offset = InitOatCodeDexFiles(offset); + + CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); + CHECK(image_file_location.empty() == compiler->IsImage()); +} + +OatWriter::~OatWriter() { + delete oat_header_; + STLDeleteElements(&oat_dex_files_); + STLDeleteElements(&oat_classes_); +} + +size_t OatWriter::InitOatHeader() { + // create the OatHeader + oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(), + dex_files_, + image_file_location_oat_checksum_, + image_file_location_oat_begin_, + image_file_location_); + size_t offset = sizeof(*oat_header_); + offset += image_file_location_.size(); + return offset; +} + +size_t OatWriter::InitOatDexFiles(size_t offset) { + // create the OatDexFiles + for (size_t i = 0; i != dex_files_->size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + CHECK(dex_file != NULL); + OatDexFile* oat_dex_file = new OatDexFile(offset, *dex_file); + oat_dex_files_.push_back(oat_dex_file); + offset += oat_dex_file->SizeOf(); + } + return offset; +} + +size_t OatWriter::InitDexFiles(size_t offset) { + // calculate the offsets within OatDexFiles to the DexFiles + for (size_t i = 0; i != dex_files_->size(); ++i) { + // dex files are required to be 4 byte aligned + size_t original_offset = offset; + offset = RoundUp(offset, 4); + size_dex_file_alignment_ += offset - original_offset; + + // set offset in OatDexFile to DexFile + oat_dex_files_[i]->dex_file_offset_ = offset; + + const DexFile* dex_file = (*dex_files_)[i]; + offset += dex_file->GetHeader().file_size_; + } + return offset; +} + +size_t OatWriter::InitOatClasses(size_t offset) { + // create the OatClasses + // calculate the offsets within OatDexFiles to OatClasses + for (size_t i = 0; i != dex_files_->size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + for (size_t class_def_index = 0; + class_def_index < dex_file->NumClassDefs(); + class_def_index++) { + oat_dex_files_[i]->methods_offsets_[class_def_index] = offset; + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const byte* class_data = dex_file->GetClassData(class_def); + uint32_t num_methods = 0; + if (class_data != NULL) { // ie not an empty class, such as a marker interface + ClassDataItemIterator it(*dex_file, class_data); + size_t num_direct_methods = it.NumDirectMethods(); + size_t num_virtual_methods = it.NumVirtualMethods(); + num_methods = num_direct_methods + num_virtual_methods; + } + + ClassReference class_ref(dex_file, class_def_index); + CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + + OatClass* oat_class = new OatClass(offset, status, num_methods); + oat_classes_.push_back(oat_class); + offset += oat_class->SizeOf(); + } + oat_dex_files_[i]->UpdateChecksum(*oat_header_); + } + return offset; +} + +size_t OatWriter::InitOatCode(size_t offset) { + // calculate the offsets within OatHeader to executable code + size_t old_offset = offset; + // required to be on a new page boundary + offset = RoundUp(offset, kPageSize); + oat_header_->SetExecutableOffset(offset); + size_executable_offset_alignment_ = offset - old_offset; + if (compiler_driver_->IsImage()) { + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + oat_header_->SetInterpreterToInterpreterEntryOffset(offset); + interpreter_to_interpreter_entry_.reset(compiler_driver_->CreateInterpreterToInterpreterEntry()); + offset += interpreter_to_interpreter_entry_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetInterpreterToQuickEntryOffset(offset); + interpreter_to_quick_entry_.reset(compiler_driver_->CreateInterpreterToQuickEntry()); + offset += interpreter_to_quick_entry_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetPortableResolutionTrampolineOffset(offset); + portable_resolution_trampoline_.reset(compiler_driver_->CreatePortableResolutionTrampoline()); + offset += portable_resolution_trampoline_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetQuickResolutionTrampolineOffset(offset); + quick_resolution_trampoline_.reset(compiler_driver_->CreateQuickResolutionTrampoline()); + offset += quick_resolution_trampoline_->size(); + } else { + oat_header_->SetInterpreterToInterpreterEntryOffset(0); + oat_header_->SetInterpreterToQuickEntryOffset(0); + oat_header_->SetPortableResolutionTrampolineOffset(0); + oat_header_->SetQuickResolutionTrampolineOffset(0); + } + return offset; +} + +size_t OatWriter::InitOatCodeDexFiles(size_t offset) { + size_t oat_class_index = 0; + for (size_t i = 0; i != dex_files_->size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + CHECK(dex_file != NULL); + offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file); + } + return offset; +} + +size_t OatWriter::InitOatCodeDexFile(size_t offset, + size_t& oat_class_index, + const DexFile& dex_file) { + for (size_t class_def_index = 0; + class_def_index < dex_file.NumClassDefs(); + class_def_index++, oat_class_index++) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + offset = InitOatCodeClassDef(offset, oat_class_index, class_def_index, dex_file, class_def); + oat_classes_[oat_class_index]->UpdateChecksum(*oat_header_); + } + return offset; +} + +size_t OatWriter::InitOatCodeClassDef(size_t offset, + size_t oat_class_index, size_t class_def_index, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) { + const byte* class_data = dex_file.GetClassData(class_def); + if (class_data == NULL) { + // empty class, such as a marker interface + return offset; + } + ClassDataItemIterator it(dex_file, class_data); + CHECK_EQ(oat_classes_[oat_class_index]->method_offsets_.size(), + it.NumDirectMethods() + it.NumVirtualMethods()); + // Skip fields + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + // Process methods + size_t class_def_method_index = 0; + while (it.HasNextDirectMethod()) { + bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; + offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, + is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(), + &dex_file); + class_def_method_index++; + it.Next(); + } + while (it.HasNextVirtualMethod()) { + bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; + offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, + is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(), + &dex_file); + class_def_method_index++; + it.Next(); + } + DCHECK(!it.HasNext()); + return offset; +} + +size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, + size_t __attribute__((unused)) class_def_index, + size_t class_def_method_index, + bool __attribute__((unused)) is_native, + InvokeType invoke_type, + uint32_t method_idx, const DexFile* dex_file) { + // derived from CompiledMethod if available + uint32_t code_offset = 0; + uint32_t frame_size_in_bytes = kStackAlignment; + uint32_t core_spill_mask = 0; + uint32_t fp_spill_mask = 0; + uint32_t mapping_table_offset = 0; + uint32_t vmap_table_offset = 0; + uint32_t gc_map_offset = 0; + + OatClass* oat_class = oat_classes_[oat_class_index]; +#if defined(ART_USE_PORTABLE_COMPILER) + size_t oat_method_offsets_offset = + oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index); +#endif + + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); + if (compiled_method != NULL) { +#if defined(ART_USE_PORTABLE_COMPILER) + compiled_method->AddOatdataOffsetToCompliledCodeOffset( + oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_)); +#else + const std::vector<uint8_t>& code = compiled_method->GetCode(); + offset = compiled_method->AlignCode(offset); + DCHECK_ALIGNED(offset, kArmAlignment); + uint32_t code_size = code.size() * sizeof(code[0]); + CHECK_NE(code_size, 0U); + uint32_t thumb_offset = compiled_method->CodeDelta(); + code_offset = offset + sizeof(code_size) + thumb_offset; + + // Deduplicate code arrays + SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = code_offsets_.find(&code); + if (code_iter != code_offsets_.end()) { + code_offset = code_iter->second; + } else { + code_offsets_.Put(&code, code_offset); + offset += sizeof(code_size); // code size is prepended before code + offset += code_size; + oat_header_->UpdateChecksum(&code[0], code_size); + } +#endif + frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); + core_spill_mask = compiled_method->GetCoreSpillMask(); + fp_spill_mask = compiled_method->GetFpSpillMask(); + + const std::vector<uint32_t>& mapping_table = compiled_method->GetMappingTable(); + size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]); + mapping_table_offset = (mapping_table_size == 0) ? 0 : offset; + + // Deduplicate mapping tables + SafeMap<const std::vector<uint32_t>*, uint32_t>::iterator mapping_iter = mapping_table_offsets_.find(&mapping_table); + if (mapping_iter != mapping_table_offsets_.end()) { + mapping_table_offset = mapping_iter->second; + } else { + mapping_table_offsets_.Put(&mapping_table, mapping_table_offset); + offset += mapping_table_size; + oat_header_->UpdateChecksum(&mapping_table[0], mapping_table_size); + } + + const std::vector<uint16_t>& vmap_table = compiled_method->GetVmapTable(); + size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]); + vmap_table_offset = (vmap_table_size == 0) ? 0 : offset; + + // Deduplicate vmap tables + SafeMap<const std::vector<uint16_t>*, uint32_t>::iterator vmap_iter = vmap_table_offsets_.find(&vmap_table); + if (vmap_iter != vmap_table_offsets_.end()) { + vmap_table_offset = vmap_iter->second; + } else { + vmap_table_offsets_.Put(&vmap_table, vmap_table_offset); + offset += vmap_table_size; + oat_header_->UpdateChecksum(&vmap_table[0], vmap_table_size); + } + + const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); + size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); + gc_map_offset = (gc_map_size == 0) ? 0 : offset; + +#if !defined(NDEBUG) + // We expect GC maps except when the class hasn't been verified or the method is native + ClassReference class_ref(dex_file, class_def_index); + CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) + << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " + << (status < mirror::Class::kStatusVerified) << " " << status << " " + << PrettyMethod(method_idx, *dex_file); +#endif + + // Deduplicate GC maps + SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = gc_map_offsets_.find(&gc_map); + if (gc_map_iter != gc_map_offsets_.end()) { + gc_map_offset = gc_map_iter->second; + } else { + gc_map_offsets_.Put(&gc_map, gc_map_offset); + offset += gc_map_size; + oat_header_->UpdateChecksum(&gc_map[0], gc_map_size); + } + } + + oat_class->method_offsets_[class_def_method_index] + = OatMethodOffsets(code_offset, + frame_size_in_bytes, + core_spill_mask, + fp_spill_mask, + mapping_table_offset, + vmap_table_offset, + gc_map_offset + ); + + if (compiler_driver_->IsImage()) { + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); + // Unchecked as we hold mutator_lock_ on entry. + ScopedObjectAccessUnchecked soa(Thread::Current()); + mirror::AbstractMethod* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, + NULL, NULL, invoke_type); + CHECK(method != NULL); + method->SetFrameSizeInBytes(frame_size_in_bytes); + method->SetCoreSpillMask(core_spill_mask); + method->SetFpSpillMask(fp_spill_mask); + method->SetOatMappingTableOffset(mapping_table_offset); + // Don't overwrite static method trampoline + if (!method->IsStatic() || method->IsConstructor() || + method->GetDeclaringClass()->IsInitialized()) { + method->SetOatCodeOffset(code_offset); + } else { + method->SetEntryPointFromCompiledCode(NULL); + } + method->SetOatVmapTableOffset(vmap_table_offset); + method->SetOatNativeGcMapOffset(gc_map_offset); + } + + return offset; +} + +#define DCHECK_OFFSET() \ + DCHECK_EQ(static_cast<off_t>(offset), out.Seek(0, kSeekCurrent)) + +#define DCHECK_OFFSET_() \ + DCHECK_EQ(static_cast<off_t>(offset_), out.Seek(0, kSeekCurrent)) + +bool OatWriter::Write(OutputStream& out) { + if (!out.WriteFully(oat_header_, sizeof(*oat_header_))) { + PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation(); + return false; + } + size_oat_header_ += sizeof(*oat_header_); + + if (!out.WriteFully(image_file_location_.data(), image_file_location_.size())) { + PLOG(ERROR) << "Failed to write oat header image file location to " << out.GetLocation(); + return false; + } + size_oat_header_image_file_location_ += image_file_location_.size(); + + if (!WriteTables(out)) { + LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation(); + return false; + } + + size_t code_offset = WriteCode(out); + if (code_offset == 0) { + LOG(ERROR) << "Failed to write oat code to " << out.GetLocation(); + return false; + } + + code_offset = WriteCodeDexFiles(out, code_offset); + if (code_offset == 0) { + LOG(ERROR) << "Failed to write oat code for dex files to " << out.GetLocation(); + return false; + } + + if (kIsDebugBuild) { + uint32_t size_total = 0; + #define DO_STAT(x) \ + LOG(INFO) << #x "=" << PrettySize(x) << " (" << x << "B)"; \ + size_total += x; + + DO_STAT(size_dex_file_alignment_); + DO_STAT(size_executable_offset_alignment_); + DO_STAT(size_oat_header_); + DO_STAT(size_oat_header_image_file_location_); + DO_STAT(size_dex_file_); + DO_STAT(size_interpreter_to_interpreter_entry_); + DO_STAT(size_interpreter_to_quick_entry_); + DO_STAT(size_portable_resolution_trampoline_); + DO_STAT(size_quick_resolution_trampoline_); + DO_STAT(size_stubs_alignment_); + DO_STAT(size_code_size_); + DO_STAT(size_code_); + DO_STAT(size_code_alignment_); + DO_STAT(size_mapping_table_); + DO_STAT(size_vmap_table_); + DO_STAT(size_gc_map_); + DO_STAT(size_oat_dex_file_location_size_); + DO_STAT(size_oat_dex_file_location_data_); + DO_STAT(size_oat_dex_file_location_checksum_); + DO_STAT(size_oat_dex_file_offset_); + DO_STAT(size_oat_dex_file_methods_offsets_); + DO_STAT(size_oat_class_status_); + DO_STAT(size_oat_class_method_offsets_); + #undef DO_STAT + + LOG(INFO) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \ + CHECK_EQ(size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent))); + } + + return true; +} + +bool OatWriter::WriteTables(OutputStream& out) { + for (size_t i = 0; i != oat_dex_files_.size(); ++i) { + if (!oat_dex_files_[i]->Write(this, out)) { + PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation(); + return false; + } + } + for (size_t i = 0; i != oat_dex_files_.size(); ++i) { + uint32_t expected_offset = oat_dex_files_[i]->dex_file_offset_; + off_t actual_offset = out.Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + const DexFile* dex_file = (*dex_files_)[i]; + PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << dex_file->GetLocation(); + return false; + } + const DexFile* dex_file = (*dex_files_)[i]; + if (!out.WriteFully(&dex_file->GetHeader(), dex_file->GetHeader().file_size_)) { + PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() << " to " << out.GetLocation(); + return false; + } + size_dex_file_ += dex_file->GetHeader().file_size_; + } + for (size_t i = 0; i != oat_classes_.size(); ++i) { + if (!oat_classes_[i]->Write(this, out)) { + PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation(); + return false; + } + } + return true; +} + +size_t OatWriter::WriteCode(OutputStream& out) { + uint32_t offset = oat_header_->GetExecutableOffset(); + off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent); + if (static_cast<uint32_t>(new_offset) != offset) { + PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset + << " Expected: " << offset << " File: " << out.GetLocation(); + return 0; + } + DCHECK_OFFSET(); + if (compiler_driver_->IsImage()) { + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0], interpreter_to_interpreter_entry_->size())) { + PLOG(ERROR) << "Failed to write interpreter to interpreter entry to " << out.GetLocation(); + return false; + } + size_interpreter_to_interpreter_entry_ += interpreter_to_interpreter_entry_->size(); + offset += interpreter_to_interpreter_entry_->size(); + DCHECK_OFFSET(); + + uint32_t aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + uint32_t alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*interpreter_to_quick_entry_)[0], interpreter_to_quick_entry_->size())) { + PLOG(ERROR) << "Failed to write interpreter to quick entry to " << out.GetLocation(); + return false; + } + size_interpreter_to_quick_entry_ += interpreter_to_quick_entry_->size(); + offset += alignment_padding + interpreter_to_quick_entry_->size(); + DCHECK_OFFSET(); + + aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*portable_resolution_trampoline_)[0], portable_resolution_trampoline_->size())) { + PLOG(ERROR) << "Failed to write portable resolution trampoline to " << out.GetLocation(); + return false; + } + size_portable_resolution_trampoline_ += portable_resolution_trampoline_->size(); + offset += alignment_padding + portable_resolution_trampoline_->size(); + DCHECK_OFFSET(); + + aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*quick_resolution_trampoline_)[0], quick_resolution_trampoline_->size())) { + PLOG(ERROR) << "Failed to write quick resolution trampoline to " << out.GetLocation(); + return false; + } + size_quick_resolution_trampoline_ += quick_resolution_trampoline_->size(); + offset += alignment_padding + quick_resolution_trampoline_->size(); + DCHECK_OFFSET(); + } + return offset; +} + +size_t OatWriter::WriteCodeDexFiles(OutputStream& out, size_t code_offset) { + size_t oat_class_index = 0; + for (size_t i = 0; i != oat_dex_files_.size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + CHECK(dex_file != NULL); + code_offset = WriteCodeDexFile(out, code_offset, oat_class_index, *dex_file); + if (code_offset == 0) { + return 0; + } + } + return code_offset; +} + +size_t OatWriter::WriteCodeDexFile(OutputStream& out, size_t code_offset, size_t& oat_class_index, + const DexFile& dex_file) { + for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); + class_def_index++, oat_class_index++) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + code_offset = WriteCodeClassDef(out, code_offset, oat_class_index, dex_file, class_def); + if (code_offset == 0) { + return 0; + } + } + return code_offset; +} + +void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx, + const DexFile& dex_file, OutputStream& out) const { + PLOG(ERROR) << "Failed to write " << what << " for " << PrettyMethod(method_idx, dex_file) + << " to " << out.GetLocation(); +} + +size_t OatWriter::WriteCodeClassDef(OutputStream& out, + size_t code_offset, size_t oat_class_index, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) { + const byte* class_data = dex_file.GetClassData(class_def); + if (class_data == NULL) { + // ie. an empty class such as a marker interface + return code_offset; + } + ClassDataItemIterator it(dex_file, class_data); + // Skip fields + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + // Process methods + size_t class_def_method_index = 0; + while (it.HasNextDirectMethod()) { + bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0; + code_offset = WriteCodeMethod(out, code_offset, oat_class_index, class_def_method_index, + is_static, it.GetMemberIndex(), dex_file); + if (code_offset == 0) { + return 0; + } + class_def_method_index++; + it.Next(); + } + while (it.HasNextVirtualMethod()) { + code_offset = WriteCodeMethod(out, code_offset, oat_class_index, class_def_method_index, + false, it.GetMemberIndex(), dex_file); + if (code_offset == 0) { + return 0; + } + class_def_method_index++; + it.Next(); + } + return code_offset; +} + +size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_class_index, + size_t class_def_method_index, bool is_static, + uint32_t method_idx, const DexFile& dex_file) { + const CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx)); + + OatMethodOffsets method_offsets = + oat_classes_[oat_class_index]->method_offsets_[class_def_method_index]; + + + if (compiled_method != NULL) { // ie. not an abstract method +#if !defined(ART_USE_PORTABLE_COMPILER) + uint32_t aligned_offset = compiled_method->AlignCode(offset); + uint32_t aligned_code_delta = aligned_offset - offset; + if (aligned_code_delta != 0) { + off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent); + size_code_alignment_ += aligned_code_delta; + if (static_cast<uint32_t>(new_offset) != aligned_offset) { + PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset + << " Expected: " << aligned_offset << " File: " << out.GetLocation(); + return 0; + } + offset += aligned_code_delta; + DCHECK_OFFSET(); + } + DCHECK_ALIGNED(offset, kArmAlignment); + const std::vector<uint8_t>& code = compiled_method->GetCode(); + uint32_t code_size = code.size() * sizeof(code[0]); + CHECK_NE(code_size, 0U); + + // Deduplicate code arrays + size_t code_offset = offset + sizeof(code_size) + compiled_method->CodeDelta(); + SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = code_offsets_.find(&code); + if (code_iter != code_offsets_.end() && code_offset != method_offsets.code_offset_) { + DCHECK(code_iter->second == method_offsets.code_offset_) + << PrettyMethod(method_idx, dex_file); + } else { + DCHECK(code_offset == method_offsets.code_offset_) << PrettyMethod(method_idx, dex_file); + if (!out.WriteFully(&code_size, sizeof(code_size))) { + ReportWriteFailure("method code size", method_idx, dex_file, out); + return 0; + } + size_code_size_ += sizeof(code_size); + offset += sizeof(code_size); + DCHECK_OFFSET(); + if (!out.WriteFully(&code[0], code_size)) { + ReportWriteFailure("method code", method_idx, dex_file, out); + return 0; + } + size_code_ += code_size; + offset += code_size; + } + DCHECK_OFFSET(); +#endif + + const std::vector<uint32_t>& mapping_table = compiled_method->GetMappingTable(); + size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]); + + // Deduplicate mapping tables + SafeMap<const std::vector<uint32_t>*, uint32_t>::iterator mapping_iter = + mapping_table_offsets_.find(&mapping_table); + if (mapping_iter != mapping_table_offsets_.end() && + offset != method_offsets.mapping_table_offset_) { + DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) + || mapping_iter->second == method_offsets.mapping_table_offset_) + << PrettyMethod(method_idx, dex_file); + } else { + DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) + || offset == method_offsets.mapping_table_offset_) + << PrettyMethod(method_idx, dex_file); + if (!out.WriteFully(&mapping_table[0], mapping_table_size)) { + ReportWriteFailure("mapping table", method_idx, dex_file, out); + return 0; + } + size_mapping_table_ += mapping_table_size; + offset += mapping_table_size; + } + DCHECK_OFFSET(); + + const std::vector<uint16_t>& vmap_table = compiled_method->GetVmapTable(); + size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]); + + // Deduplicate vmap tables + SafeMap<const std::vector<uint16_t>*, uint32_t>::iterator vmap_iter = + vmap_table_offsets_.find(&vmap_table); + if (vmap_iter != vmap_table_offsets_.end() && + offset != method_offsets.vmap_table_offset_) { + DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) + || vmap_iter->second == method_offsets.vmap_table_offset_) + << PrettyMethod(method_idx, dex_file); + } else { + DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) + || offset == method_offsets.vmap_table_offset_) + << PrettyMethod(method_idx, dex_file); + if (!out.WriteFully(&vmap_table[0], vmap_table_size)) { + ReportWriteFailure("vmap table", method_idx, dex_file, out); + return 0; + } + size_vmap_table_ += vmap_table_size; + offset += vmap_table_size; + } + DCHECK_OFFSET(); + + const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); + size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); + + // Deduplicate GC maps + SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = + gc_map_offsets_.find(&gc_map); + if (gc_map_iter != gc_map_offsets_.end() && + offset != method_offsets.gc_map_offset_) { + DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) + || gc_map_iter->second == method_offsets.gc_map_offset_) + << PrettyMethod(method_idx, dex_file); + } else { + DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) + || offset == method_offsets.gc_map_offset_) + << PrettyMethod(method_idx, dex_file); + if (!out.WriteFully(&gc_map[0], gc_map_size)) { + ReportWriteFailure("GC map", method_idx, dex_file, out); + return 0; + } + size_gc_map_ += gc_map_size; + offset += gc_map_size; + } + DCHECK_OFFSET(); + } + + return offset; +} + +OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) { + offset_ = offset; + const std::string& location(dex_file.GetLocation()); + dex_file_location_size_ = location.size(); + dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data()); + dex_file_location_checksum_ = dex_file.GetLocationChecksum(); + dex_file_offset_ = 0; + methods_offsets_.resize(dex_file.NumClassDefs()); +} + +size_t OatWriter::OatDexFile::SizeOf() const { + return sizeof(dex_file_location_size_) + + dex_file_location_size_ + + sizeof(dex_file_location_checksum_) + + sizeof(dex_file_offset_) + + (sizeof(methods_offsets_[0]) * methods_offsets_.size()); +} + +void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const { + oat_header.UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_)); + oat_header.UpdateChecksum(dex_file_location_data_, dex_file_location_size_); + oat_header.UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_)); + oat_header.UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_)); + oat_header.UpdateChecksum(&methods_offsets_[0], + sizeof(methods_offsets_[0]) * methods_offsets_.size()); +} + +bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream& out) const { + DCHECK_OFFSET_(); + if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) { + PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_); + if (!out.WriteFully(dex_file_location_data_, dex_file_location_size_)) { + PLOG(ERROR) << "Failed to write dex file location data to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_; + if (!out.WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { + PLOG(ERROR) << "Failed to write dex file location checksum to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_); + if (!out.WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) { + PLOG(ERROR) << "Failed to write dex file offset to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); + if (!out.WriteFully(&methods_offsets_[0], + sizeof(methods_offsets_[0]) * methods_offsets_.size())) { + PLOG(ERROR) << "Failed to write methods offsets to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_methods_offsets_ += + sizeof(methods_offsets_[0]) * methods_offsets_.size(); + return true; +} + +OatWriter::OatClass::OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count) { + offset_ = offset; + status_ = status; + method_offsets_.resize(methods_count); +} + +size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader( + size_t class_def_method_index_) const { + return offset_ + GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_); +} + +size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass( + size_t class_def_method_index_) const { + return sizeof(status_) + + (sizeof(method_offsets_[0]) * class_def_method_index_); +} + +size_t OatWriter::OatClass::SizeOf() const { + return GetOatMethodOffsetsOffsetFromOatClass(method_offsets_.size()); +} + +void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const { + oat_header.UpdateChecksum(&status_, sizeof(status_)); + oat_header.UpdateChecksum(&method_offsets_[0], + sizeof(method_offsets_[0]) * method_offsets_.size()); +} + +bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream& out) const { + DCHECK_OFFSET_(); + if (!out.WriteFully(&status_, sizeof(status_))) { + PLOG(ERROR) << "Failed to write class status to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_class_status_ += sizeof(status_); + DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(0)), + out.Seek(0, kSeekCurrent)); + if (!out.WriteFully(&method_offsets_[0], + sizeof(method_offsets_[0]) * method_offsets_.size())) { + PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size(); + DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())), + out.Seek(0, kSeekCurrent)); + return true; +} + +} // namespace art diff --git a/src/compiler/oat_writer.h b/src/compiler/oat_writer.h new file mode 100644 index 0000000000..b201d6b4ee --- /dev/null +++ b/src/compiler/oat_writer.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_OAT_WRITER_H_ +#define ART_SRC_OAT_WRITER_H_ + +#include <stdint.h> + +#include <cstddef> + +#include "compiler/driver/compiler_driver.h" +#include "mem_map.h" +#include "oat.h" +#include "mirror/class.h" +#include "safe_map.h" +#include "UniquePtr.h" + +namespace art { + +class OutputStream; + +// OatHeader variable length with count of D OatDexFiles +// +// OatDexFile[0] one variable sized OatDexFile with offsets to Dex and OatClasses +// OatDexFile[1] +// ... +// OatDexFile[D] +// +// Dex[0] one variable sized DexFile for each OatDexFile. +// Dex[1] these are literal copies of the input .dex files. +// ... +// Dex[D] +// +// OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs +// OatClass[1] contains OatClass entries with class status, offsets to code, etc. +// ... +// OatClass[C] +// +// padding if necessary so that the following code will be page aligned +// +// CompiledMethod one variable sized blob with the contents of each CompiledMethod +// CompiledMethod +// CompiledMethod +// CompiledMethod +// CompiledMethod +// CompiledMethod +// ... +// CompiledMethod +// +class OatWriter { + public: + // Write an oat file. Returns true on success, false on failure. + static bool Create(OutputStream& out, + const std::vector<const DexFile*>& dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_begin, + const std::string& image_file_location, + const CompilerDriver& compiler) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + OatWriter(const std::vector<const DexFile*>& dex_files, + uint32_t image_file_location_oat_checksum, + uint32_t image_file_location_oat_begin, + const std::string& image_file_location, + const CompilerDriver* compiler) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ~OatWriter(); + + size_t InitOatHeader(); + size_t InitOatDexFiles(size_t offset); + size_t InitDexFiles(size_t offset); + size_t InitOatClasses(size_t offset); + size_t InitOatCode(size_t offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t InitOatCodeDexFiles(size_t offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t InitOatCodeDexFile(size_t offset, + size_t& oat_class_index, + const DexFile& dex_file) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t InitOatCodeClassDef(size_t offset, + size_t oat_class_index, size_t class_def_index, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index, + size_t class_def_method_index, bool is_native, InvokeType type, + uint32_t method_idx, const DexFile*) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool Write(OutputStream& out); + bool WriteTables(OutputStream& out); + size_t WriteCode(OutputStream& out); + size_t WriteCodeDexFiles(OutputStream& out, size_t offset); + size_t WriteCodeDexFile(OutputStream& out, size_t offset, size_t& oat_class_index, + const DexFile& dex_file); + size_t WriteCodeClassDef(OutputStream& out, size_t offset, size_t oat_class_index, + const DexFile& dex_file, const DexFile::ClassDef& class_def); + size_t WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_class_index, + size_t class_def_method_index, bool is_static, uint32_t method_idx, + const DexFile& dex_file); + + void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file, + OutputStream& out) const; + + class OatDexFile { + public: + explicit OatDexFile(size_t offset, const DexFile& dex_file); + size_t SizeOf() const; + void UpdateChecksum(OatHeader& oat_header) const; + bool Write(OatWriter* oat_writer, OutputStream& out) const; + + // Offset of start of OatDexFile from beginning of OatHeader. It is + // used to validate file position when writing. + size_t offset_; + + // data to write + uint32_t dex_file_location_size_; + const uint8_t* dex_file_location_data_; + uint32_t dex_file_location_checksum_; + uint32_t dex_file_offset_; + std::vector<uint32_t> methods_offsets_; + + private: + DISALLOW_COPY_AND_ASSIGN(OatDexFile); + }; + + class OatClass { + public: + explicit OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count); + size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const; + size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const; + size_t SizeOf() const; + void UpdateChecksum(OatHeader& oat_header) const; + bool Write(OatWriter* oat_writer, OutputStream& out) const; + + // Offset of start of OatClass from beginning of OatHeader. It is + // used to validate file position when writing. For Portable, it + // is also used to calculate the position of the OatMethodOffsets + // so that code pointers within the OatMethodOffsets can be + // patched to point to code in the Portable .o ELF objects. + size_t offset_; + + // data to write + mirror::Class::Status status_; + std::vector<OatMethodOffsets> method_offsets_; + + private: + DISALLOW_COPY_AND_ASSIGN(OatClass); + }; + + const CompilerDriver* const compiler_driver_; + + // note OatFile does not take ownership of the DexFiles + const std::vector<const DexFile*>* dex_files_; + + // dependencies on the image. + uint32_t image_file_location_oat_checksum_; + uint32_t image_file_location_oat_begin_; + std::string image_file_location_; + + // data to write + OatHeader* oat_header_; + std::vector<OatDexFile*> oat_dex_files_; + std::vector<OatClass*> oat_classes_; + UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_entry_; + UniquePtr<const std::vector<uint8_t> > interpreter_to_quick_entry_; + UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_; + UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_; + + // output stats + uint32_t size_dex_file_alignment_; + uint32_t size_executable_offset_alignment_; + uint32_t size_oat_header_; + uint32_t size_oat_header_image_file_location_; + uint32_t size_dex_file_; + uint32_t size_interpreter_to_interpreter_entry_; + uint32_t size_interpreter_to_quick_entry_; + uint32_t size_portable_resolution_trampoline_; + uint32_t size_quick_resolution_trampoline_; + uint32_t size_stubs_alignment_; + uint32_t size_code_size_; + uint32_t size_code_; + uint32_t size_code_alignment_; + uint32_t size_mapping_table_; + uint32_t size_vmap_table_; + uint32_t size_gc_map_; + uint32_t size_oat_dex_file_location_size_; + uint32_t size_oat_dex_file_location_data_; + uint32_t size_oat_dex_file_location_checksum_; + uint32_t size_oat_dex_file_offset_; + uint32_t size_oat_dex_file_methods_offsets_; + uint32_t size_oat_class_status_; + uint32_t size_oat_class_method_offsets_; + + template <class T> struct MapCompare { + public: + bool operator() (const T* const &a, const T* const &b) const { + return *a < *b; + } + }; + + // code mappings for deduplication + SafeMap<const std::vector<uint8_t>*, uint32_t, MapCompare<std::vector<uint8_t> > > code_offsets_; + SafeMap<const std::vector<uint16_t>*, uint32_t, MapCompare<std::vector<uint16_t> > > vmap_table_offsets_; + SafeMap<const std::vector<uint32_t>*, uint32_t, MapCompare<std::vector<uint32_t> > > mapping_table_offsets_; + SafeMap<const std::vector<uint8_t>*, uint32_t, MapCompare<std::vector<uint8_t> > > gc_map_offsets_; + + DISALLOW_COPY_AND_ASSIGN(OatWriter); +}; + +} // namespace art + +#endif // ART_SRC_OAT_WRITER_H_ |