blob: 5637f7c620cf191b37aa27a97875e6736e28b286 [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 <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"
#include "compiler/llvm/utils_llvm.h"
#include "dex_method_iterator.h"
#include "elf_file.h"
#include "invoke_type.h"
#include "mirror/abstract_method-inl.h"
#include "oat.h"
#include "oat_file.h"
#include "scoped_thread_state_change.h"
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) {}
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();
InvokeType invoke_type = it.GetInvokeType();
const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
const CompiledMethod* compiled_method =
compiler_driver_->GetCompiledMethod(CompilerDriver::MethodReference(&dex_file, method_idx));
if (compiled_method != NULL) {
AddCompiledCodeInput(*compiled_method);
}
const CompiledInvokeStub* compiled_invoke_stub = compiler_driver_->FindInvokeStub(invoke_type == kStatic,
shorty);
if (compiled_invoke_stub != NULL) {
AddCompiledCodeInput(*compiled_invoke_stub);
}
if (invoke_type != kStatic) {
const CompiledInvokeStub* compiled_proxy_stub = compiler_driver_->FindProxyStub(shorty);
if (compiled_proxy_stub != NULL) {
AddCompiledCodeInput(*compiled_proxy_stub);
}
}
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 oatdata_address = elf_file->FindSymbolAddress(llvm::ELF::SHT_DYNSYM,
"oatdata",
false);
CHECK_NE(0U, oatdata_address);
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();
const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
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);
}
}
const CompiledInvokeStub* compiled_invoke_stub = compiler_driver_->FindInvokeStub(invoke_type == kStatic,
shorty);
if (compiled_invoke_stub != NULL) {
uint32_t offset = FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_invoke_stub);
if (method != NULL) {
method->SetOatInvokeStubOffset(offset);
}
}
if (invoke_type != kStatic) {
const CompiledInvokeStub* compiled_proxy_stub = compiler_driver_->FindProxyStub(shorty);
if (compiled_proxy_stub != NULL) {
FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_proxy_stub);
}
}
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
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;
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;
}
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);
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);
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, &section_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) {
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 = GetOatDataAddress(elf_file.get());
CHECK_NE(0U, oat_data_offset);
}
} // namespace art