summaryrefslogtreecommitdiffstats
path: root/compiler/elf_writer_mclinker.cc
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2013-07-12 13:46:57 -0700
committerBrian Carlstrom <bdc@google.com>2013-07-12 17:49:01 -0700
commit7940e44f4517de5e2634a7e07d58d0fb26160513 (patch)
treeac90242d96229a6942f6e24ab137bc1f8f2e0025 /compiler/elf_writer_mclinker.cc
parent5cd9e3b122f276f610980cbaf0d2ad6ed4cd9088 (diff)
downloadandroid_art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.gz
android_art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.bz2
android_art-7940e44f4517de5e2634a7e07d58d0fb26160513.zip
Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees to prevent dependency creep. They can now be individually built without rebuilding the rest of the art projects. dalvikvm and jdwpspy were already this way. Builds in the art directory should behave as before, building everything including tests. Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
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