diff options
author | 2013-05-17 10:58:25 -0700 | |
---|---|---|
committer | 2013-05-21 18:37:04 -0700 | |
commit | 6a47b9dc850b903aabefcfab4adb132cb68bba2e (patch) | |
tree | 924d2ea244bb01a48620e704b181a1d9d1f4055c | |
parent | 7b15b1b6809a3dc46c69610491d1b10513f449db (diff) |
Refactor ElfWriter and introduce ElfWriterQuick
Change-Id: I3eaa1fa1085bb4d28fe342a7420272720e48274b
-rw-r--r-- | build/Android.libart-compiler.mk | 6 | ||||
-rw-r--r-- | src/compiler/dex/write_elf.cc | 19 | ||||
-rw-r--r-- | src/elf_file.cc | 24 | ||||
-rw-r--r-- | src/elf_fixup.cc | 279 | ||||
-rw-r--r-- | src/elf_fixup.h | 56 | ||||
-rw-r--r-- | src/elf_stripper.cc | 129 | ||||
-rw-r--r-- | src/elf_stripper.h | 37 | ||||
-rw-r--r-- | src/elf_writer.cc | 715 | ||||
-rw-r--r-- | src/elf_writer.h | 96 | ||||
-rw-r--r-- | src/elf_writer_mclinker.cc | 394 | ||||
-rw-r--r-- | src/elf_writer_mclinker.h | 99 | ||||
-rw-r--r-- | src/elf_writer_quick.cc | 665 | ||||
-rw-r--r-- | src/elf_writer_quick.h | 51 | ||||
-rw-r--r-- | src/elf_writer_test.cc | 13 | ||||
-rw-r--r-- | src/oat_file.cc | 6 | ||||
-rw-r--r-- | src/oat_file.h | 3 |
16 files changed, 1762 insertions, 830 deletions
diff --git a/build/Android.libart-compiler.mk b/build/Android.libart-compiler.mk index 5e34573e90..07fc14fb90 100644 --- a/build/Android.libart-compiler.mk +++ b/build/Android.libart-compiler.mk @@ -71,7 +71,11 @@ LIBART_COMPILER_SRC_FILES := \ src/compiler/llvm/runtime_support_builder_thumb2.cc \ src/compiler/llvm/runtime_support_builder_x86.cc \ src/compiler/llvm/runtime_support_llvm.cc \ - src/elf_writer.cc + src/elf_fixup.cc \ + src/elf_stripper.cc \ + src/elf_writer.cc \ + src/elf_writer_mclinker.cc \ + src/elf_writer_quick.cc LIBART_COMPILER_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) diff --git a/src/compiler/dex/write_elf.cc b/src/compiler/dex/write_elf.cc index acec531b6a..d9e45a9c19 100644 --- a/src/compiler/dex/write_elf.cc +++ b/src/compiler/dex/write_elf.cc @@ -14,9 +14,16 @@ * limitations under the License. */ -#include "elf_writer.h" +#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; @@ -29,10 +36,14 @@ extern "C" bool WriteElf(art::CompilerDriver& driver, std::vector<uint8_t>& oat_contents, art::File* file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return art::ElfWriter::Create(file, oat_contents, dex_files, android_root, is_host, driver); +#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::ElfWriter::Fixup(file, oat_data_begin); + return art::ElfFixup::Fixup(file, oat_data_begin); } extern "C" void GetOatElfInformation(art::File* file, size_t& oat_loaded_size, @@ -40,5 +51,5 @@ extern "C" void GetOatElfInformation(art::File* file, art::ElfWriter::GetOatElfInformation(file, oat_loaded_size, oat_data_offset); } extern "C" bool StripElf(art::File* file) { - return art::ElfWriter::Strip(file); + return art::ElfStripper::Strip(file); } diff --git a/src/elf_file.cc b/src/elf_file.cc index 6f9d53c839..cb4ef81be3 100644 --- a/src/elf_file.cc +++ b/src/elf_file.cc @@ -616,24 +616,28 @@ bool ElfFile::Load() { // which either was specified in the file because we already set // base_address_ after the first zero segment). if (program_header.p_vaddr == 0) { - std::string reservation_name("ElfFile reservation for "); - reservation_name += file_->GetPath(); - UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(), - NULL, GetLoadedSize(), PROT_NONE)); - CHECK(reserve.get() != NULL) << file_->GetPath(); - base_address_ = reserve->Begin(); - segments_.push_back(reserve.release()); + std::string reservation_name("ElfFile reservation for "); + reservation_name += file_->GetPath(); + UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(), + NULL, GetLoadedSize(), PROT_NONE)); + CHECK(reserve.get() != NULL) << file_->GetPath(); + base_address_ = reserve->Begin(); + segments_.push_back(reserve.release()); + } + // empty segment, nothing to map + if (program_header.p_memsz == 0) { + continue; } byte* p_vaddr = base_address_ + program_header.p_vaddr; int prot = 0; if ((program_header.p_flags & llvm::ELF::PF_X) != 0) { - prot |= PROT_EXEC; + prot |= PROT_EXEC; } if ((program_header.p_flags & llvm::ELF::PF_W) != 0) { - prot |= PROT_WRITE; + prot |= PROT_WRITE; } if ((program_header.p_flags & llvm::ELF::PF_R) != 0) { - prot |= PROT_READ; + prot |= PROT_READ; } int flags = MAP_FIXED; if (writable_) { diff --git a/src/elf_fixup.cc b/src/elf_fixup.cc new file mode 100644 index 0000000000..127bc85d2c --- /dev/null +++ b/src/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/elf_fixup.h b/src/elf_fixup.h new file mode 100644 index 0000000000..79c45c1874 --- /dev/null +++ b/src/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/elf_stripper.cc b/src/elf_stripper.cc new file mode 100644 index 0000000000..7fc662ca1d --- /dev/null +++ b/src/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/elf_stripper.h b/src/elf_stripper.h new file mode 100644 index 0000000000..b202e6e1f0 --- /dev/null +++ b/src/elf_stripper.h @@ -0,0 +1,37 @@ +/* + * 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_STRIPPER_H_ +#define ART_SRC_ELF_STRIPPER_H_ + +#include "base/macros.h" +#include "os.h" + +namespace art { + +class ElfStripper { + public: + // Strip an ELF file of unneeded debugging information. + // Returns true on success, false on failure. + static bool Strip(File* file); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper); +}; + +} // namespace art + +#endif // ART_SRC_ELF_STRIPPER_H_ diff --git a/src/elf_writer.cc b/src/elf_writer.cc index 7647da9b3b..7592eb5edb 100644 --- a/src/elf_writer.cc +++ b/src/elf_writer.cc @@ -16,17 +16,6 @@ #include "elf_writer.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" @@ -43,301 +32,12 @@ namespace art { -bool ElfWriter::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) { - ElfWriter elf_writer(driver, elf_file); - return elf_writer.Write(oat_contents, dex_files, android_root, is_host); -} - ElfWriter::ElfWriter(const CompilerDriver& driver, File* elf_file) - : compiler_driver_(&driver), elf_file_(elf_file), oat_input_(NULL) {} + : compiler_driver_(&driver), elf_file_(elf_file) {} ElfWriter::~ElfWriter() {} -bool ElfWriter::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 ElfWriter::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 ElfWriter::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 ElfWriter::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 ElfWriter::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 ElfWriter::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 ElfWriter::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; -} - -static llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file) { +llvm::ELF::Elf32_Addr ElfWriter::GetOatDataAddress(ElfFile* elf_file) { llvm::ELF::Elf32_Addr oatdata_address = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM, "oatdata", false); @@ -345,417 +45,6 @@ static llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file) { return oatdata_address; } -#if defined(ART_USE_PORTABLE_COMPILER) -void ElfWriter::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 ElfWriter::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 - -static const bool DEBUG_FIXUP = false; - -bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) { - UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false)); - CHECK(elf_file.get() != NULL); - - // Lookup "oatdata" symbol address. - ::llvm::ELF::Elf32_Addr oatdata_address = 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 ElfWriter::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) { - // TODO: C++0x auto. - for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetDynamicNum(); i++) { - ::llvm::ELF::Elf32_Dyn& elf_dyn = elf_file.GetDynamic(i); - ::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 ElfWriter::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) { - for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) { - ::llvm::ELF::Elf32_Shdr& sh = elf_file.GetSectionHeader(i); - // 0 implies that the section will not exist in the memory of the process - if (sh.sh_addr == 0) { - continue; - } - 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 ElfWriter::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) { - // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now. - for (::llvm::ELF::Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) { - ::llvm::ELF::Elf32_Phdr& ph = elf_file.GetProgramHeader(i); - CHECK_EQ(ph.p_vaddr, ph.p_paddr) << elf_file.GetFile().GetPath() << " i=" << i; - CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1)))) - << 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 ElfWriter::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dynamic) { - ::llvm::ELF::Elf32_Word section_type = dynamic ? ::llvm::ELF::SHT_DYNSYM : ::llvm::ELF::SHT_SYMTAB; - // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile - ::llvm::ELF::Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type); - CHECK(symbol_section != NULL) << elf_file.GetFile().GetPath(); - for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) { - ::llvm::ELF::Elf32_Sym& symbol = elf_file.GetSymbol(section_type, i); - if (symbol.st_value != 0) { - 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 ElfWriter::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; -} - -bool ElfWriter::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; -} - void ElfWriter::GetOatElfInformation(File* file, size_t& oat_loaded_size, size_t& oat_data_offset) { diff --git a/src/elf_writer.h b/src/elf_writer.h index 1dd0131b6f..7392e67d7f 100644 --- a/src/elf_writer.h +++ b/src/elf_writer.h @@ -20,116 +20,44 @@ #include <stdint.h> #include <cstddef> +#include <string> #include <vector> #include <llvm/Support/ELF.h> -#include "UniquePtr.h" -#include "dex_file.h" +#include "base/macros.h" #include "os.h" -namespace mcld { -class IRBuilder; -class Input; -class LDSection; -class LDSymbol; -class Linker; -class LinkerConfig; -class Module; -} // namespace mcld - namespace art { -class CompiledCode; class CompilerDriver; +class DexFile; class ElfFile; class ElfWriter { public: - // Write an ELF file. Returns true on success, false on failure. - static bool Create(File* file, - std::vector<uint8_t>& oat_contents, - const std::vector<const DexFile*>& dex_files, - const std::string& android_root, - bool is_host, - const CompilerDriver& driver) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // 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); - - // Strip an ELF file of unneeded debugging information. - // Returns true on success, false on failure. - static bool Strip(File* file); - // Looks up information about location of oat file in elf file container. // Used for ImageWriter to perform memory layout. static void GetOatElfInformation(File* file, size_t& oat_loaded_size, size_t& oat_data_offset); - private: + // 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(); - 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_); - - 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 - - // 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); + 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_; - - // 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_; - }; } // namespace art diff --git a/src/elf_writer_mclinker.cc b/src/elf_writer_mclinker.cc new file mode 100644 index 0000000000..bb3a865fcb --- /dev/null +++ b/src/elf_writer_mclinker.cc @@ -0,0 +1,394 @@ +/* + * 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 "compiler/driver/compiler_driver.h" +#include "globals.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/elf_writer_mclinker.h b/src/elf_writer_mclinker.h new file mode 100644 index 0000000000..21f23e113d --- /dev/null +++ b/src/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/elf_writer_quick.cc b/src/elf_writer_quick.cc new file mode 100644 index 0000000000..c3c5415733 --- /dev/null +++ b/src/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/elf_writer_quick.h b/src/elf_writer_quick.h new file mode 100644 index 0000000000..a1a386b3d7 --- /dev/null +++ b/src/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/elf_writer_test.cc b/src/elf_writer_test.cc index a47bc7175a..d4486d2c56 100644 --- a/src/elf_writer_test.cc +++ b/src/elf_writer_test.cc @@ -31,21 +31,10 @@ class ElfWriterTest : public CommonTest { }; #define EXPECT_ELF_FILE_ADDRESS(ef, value, name, build_map) \ - EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(::llvm::ELF::SHT_SYMTAB, name, build_map))); \ EXPECT_EQ(value, reinterpret_cast<void*>(ef->FindSymbolAddress(::llvm::ELF::SHT_DYNSYM, name, build_map))); \ EXPECT_EQ(value, ef->FindDynamicSymbolAddress(name)); \ -/* - * TODO: Reenable dlopen when it works again on MIPS. It may have broken from this change: - * commit 818d98eb563ad5d7293b8b5c40f3dabf745e611f - * Author: Brian Carlstrom <bdc@google.com> - * Date: Sun Feb 10 21:38:12 2013 -0800 - * - * Fix MIPS to use standard kPageSize=0x1000 section alignment for ELF sections - * - * Change-Id: I905f0c5f75921a65bd7426a54d6258c780d85d0e - */ -TEST_F(ElfWriterTest, DISABLED_dlsym) { +TEST_F(ElfWriterTest, dlsym) { std::string elf_filename; if (IsHost()) { const char* host_dir = getenv("ANDROID_HOST_OUT"); diff --git a/src/oat_file.cc b/src/oat_file.cc index 414813c34e..7bbf94cbab 100644 --- a/src/oat_file.cc +++ b/src/oat_file.cc @@ -346,8 +346,7 @@ const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) oat_method_offsets.fp_spill_mask_, oat_method_offsets.mapping_table_offset_, oat_method_offsets.vmap_table_offset_, - oat_method_offsets.gc_map_offset_ - ); + oat_method_offsets.gc_map_offset_); } OatFile::OatMethod::OatMethod(const byte* base, @@ -357,8 +356,7 @@ OatFile::OatMethod::OatMethod(const byte* base, const uint32_t fp_spill_mask, const uint32_t mapping_table_offset, const uint32_t vmap_table_offset, - const uint32_t gc_map_offset - ) + const uint32_t gc_map_offset) : begin_(base), code_offset_(code_offset), frame_size_in_bytes_(frame_size_in_bytes), diff --git a/src/oat_file.h b/src/oat_file.h index 80a2920c8f..46aad4ffd1 100644 --- a/src/oat_file.h +++ b/src/oat_file.h @@ -115,8 +115,7 @@ class OatFile { const uint32_t fp_spill_mask, const uint32_t mapping_table_offset, const uint32_t vmap_table_offset, - const uint32_t gc_map_offset - ); + const uint32_t gc_map_offset); private: template<class T> |