| /* |
| * 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, §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) { |
| 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 |