summaryrefslogtreecommitdiff
path: root/compiler/elf_writer_mclinker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/elf_writer_mclinker.cc')
-rw-r--r--compiler/elf_writer_mclinker.cc401
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