blob: 19bd232a5e8a5eabf3dedb4eabf4d049e1d58d3e [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "elf_writer.h"
#include "base/unix_file/fd_file.h"
#include "elf_file.h"
#include "oat.h"
#include "oat_file.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>
namespace art {
bool ElfWriter::Create(File* file, std::vector<uint8_t>& oat_contents, const Compiler& compiler) {
ElfWriter elf_writer(compiler);
return elf_writer.Write(oat_contents, file);
}
ElfWriter::ElfWriter(const Compiler& compiler) : compiler_(&compiler) {}
ElfWriter::~ElfWriter() {}
static void InitializeLLVM() {
// TODO: this is lifted from art's compiler_llvm.cc, should be factored out
#if defined(ART_TARGET)
llvm::InitializeNativeTarget();
// TODO: odd that there is no InitializeNativeTargetMC?
#else
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
#endif
}
bool ElfWriter::Write(std::vector<uint8_t>& oat_contents, File* elf_file) {
std::string target_triple;
std::string target_cpu;
std::string target_attr;
Compiler::InstructionSetToLLVMTarget(compiler_->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();
UniquePtr<mcld::LinkerConfig> linker_config(new mcld::LinkerConfig(target_triple));
CHECK(linker_config.get() != NULL);
linker_config->setCodeGenType(mcld::LinkerConfig::DynObj);
if (compiler_->GetInstructionSet() == kMips) {
// MCLinker defaults MIPS section alignment to 0x10000, not 0x1000
mcld::ZOption z_option;
z_option.setKind(mcld::ZOption::MaxPageSize);
z_option.setPageSize(kPageSize);
linker_config->options().addZOption(z_option);
}
linker_config->options().setSOName(elf_file->GetPath());
// TODO: Wire up mcld DiagnosticEngine to LOG?
if (false) {
// enables some tracing of input file processing
linker_config->options().setTrace(true);
}
// Based on alone::Linker::config
UniquePtr<mcld::Module> module(new mcld::Module(linker_config->options().soname()));
CHECK(module.get() != NULL);
UniquePtr<mcld::IRBuilder> ir_builder(new mcld::IRBuilder(*module.get(), *linker_config.get()));
CHECK(ir_builder.get() != NULL);
UniquePtr<mcld::Linker> linker(new mcld::Linker());
CHECK(linker.get() != NULL);
linker->config(*linker_config.get());
// Add an artificial memory input. Based on LinkerTest.
UniquePtr<OatFile> oat_file(OatFile::Open(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 input?
mcld::Input* input = ir_builder->CreateInput("oat contents",
mcld::sys::fs::Path("oat contents path"),
mcld::Input::Object);
CHECK(input != NULL);
// TODO: ownership of null_section?
mcld::LDSection* null_section = ir_builder->CreateELFHeader(*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);
// 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(*input,
".text",
llvm::ELF::SHT_PROGBITS,
llvm::ELF::SHF_EXECINSTR
| llvm::ELF::SHF_ALLOC,
alignment);
CHECK(text_section != NULL);
mcld::SectionData* text_section_data = ir_builder->CreateSectionData(*text_section);
CHECK(text_section_data != 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_section_data);
ir_builder->AddSymbol(*input,
"oatdata",
mcld::ResolveInfo::Object,
mcld::ResolveInfo::Define,
mcld::ResolveInfo::Global,
oat_data_length, // size
0, // offset
text_section);
ir_builder->AddSymbol(*input,
"oatexec",
mcld::ResolveInfo::Function,
mcld::ResolveInfo::Define,
mcld::ResolveInfo::Global,
oat_code_length, // size
oat_data_length, // offset
text_section);
ir_builder->AddSymbol(*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);
// link inputs
if (!linker->link(*module.get(), *ir_builder.get())) {
LOG(ERROR) << "problem linking " << elf_file->GetPath();
return false;
}
// emited linked output
if (!linker->emit(elf_file->Fd())) {
LOG(ERROR) << "problem emitting " << elf_file->GetPath();
return false;
}
// TODO: mcld::Linker::emit closed the file descriptor. It probably shouldn't.
// For now, close our File to match.
elf_file->Close();
mcld::Finalize();
}
LOG(INFO) << "ELF file written successfully: " << elf_file->GetPath();
return true;
}
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 = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM,
"oatdata");
CHECK_NE(0U, oatdata_address);
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;
}
return true;
}
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);
bool elf_dyn_needs_fixup = false;
// case 1: if Elf32_Dyn.d_tag implies Elf32_Dyn.d_un contains an address in d_ptr
switch (elf_dyn.d_tag) {
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;
}
default: {
// case 2: if d_tag is even and greater than > DT_ENCODING
if ((elf_dyn.d_tag > llvm::ELF::DT_ENCODING) && ((elf_dyn.d_tag % 2) == 0)) {
elf_dyn_needs_fixup = true;
}
break;
}
}
if (elf_dyn_needs_fixup) {
uint32_t d_ptr = elf_dyn.d_un.d_ptr;
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;
}
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))));
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))));
}
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) {
symbol.st_value += base_address;
}
}
return true;
}
void ElfWriter::GetOatElfInformation(File* file,
size_t& oat_loaded_size,
size_t& oat_data_offset) {
UniquePtr<ElfFile> elf_file(ElfFile::Open(file, false, false));
CHECK(elf_file.get() != NULL);
oat_loaded_size = elf_file->GetLoadedSize();
CHECK_NE(0U, oat_loaded_size);
oat_data_offset = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM, "oatdata");
CHECK_NE(0U, oat_data_offset);
}
} // namespace art