diff options
Diffstat (limited to 'compiler/elf_writer_mclinker.cc')
-rw-r--r-- | compiler/elf_writer_mclinker.cc | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc new file mode 100644 index 0000000000..472a606cc6 --- /dev/null +++ b/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 "dex_method_iterator.h" +#include "driver/compiler_driver.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(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(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 |