Chromium Code Reviews| Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_shared_library.cpp |
| diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_shared_library.cpp b/third_party/crazy_linker/crazy_linker/src/crazy_linker_shared_library.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f685c43f1898daeaaf02b7ffefc4b9ca390879eb |
| --- /dev/null |
| +++ b/third_party/crazy_linker/crazy_linker/src/crazy_linker_shared_library.cpp |
| @@ -0,0 +1,714 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "crazy_linker_shared_library.h" |
| + |
| +#include <dlfcn.h> |
| +#include <stdlib.h> |
| +#include <sys/mman.h> |
| +#include <elf.h> |
| +#include <sys/exec_elf.h> |
| + |
| +#include "crazy_linker_ashmem.h" |
| +#include "crazy_linker_debug.h" |
| +#include "crazy_linker_elf_loader.h" |
| +#include "crazy_linker_elf_relocator.h" |
| +#include "crazy_linker_library_list.h" |
| +#include "crazy_linker_library_view.h" |
| +#include "crazy_linker_globals.h" |
| +#include "crazy_linker_thread.h" |
| +#include "crazy_linker_util.h" |
| +#include "crazy_linker_wrappers.h" |
| +#include "linker_phdr.h" |
| + |
| +#ifndef DF_SYMBOLIC |
| +#define DF_SYMBOLIC 2 |
| +#endif |
| + |
| +#ifndef DF_TEXTREL |
| +#define DF_TEXTREL 4 |
| +#endif |
| + |
| +#ifndef DT_INIT_ARRAY |
| +#define DT_INIT_ARRAY 25 |
| +#endif |
| + |
| +#ifndef DT_INIT_ARRAYSZ |
| +#define DT_INIT_ARRAYSZ 27 |
| +#endif |
| + |
| +#ifndef DT_FINI_ARRAY |
| +#define DT_FINI_ARRAY 26 |
| +#endif |
| + |
| +#ifndef DT_FINI_ARRAYSZ |
| +#define DT_FINI_ARRAYSZ 28 |
| +#endif |
| + |
| +#ifndef DT_FLAGS |
| +#define DT_FLAGS 30 |
| +#endif |
| + |
| +#ifndef DT_PREINIT_ARRAY |
| +#define DT_PREINIT_ARRAY 32 |
| +#endif |
| + |
| +#ifndef DT_PREINIT_ARRAYSZ |
| +#define DT_PREINIT_ARRAYSZ 33 |
| +#endif |
| + |
| +namespace crazy { |
| + |
| +namespace { |
| + |
| +typedef SharedLibrary::linker_function_t linker_function_t; |
| + |
| +// Compute the ELF hash of a given symbol. |
| +unsigned ElfHash(const char* name) { |
| + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name); |
| + unsigned h = 0; |
| + while(*ptr) { |
| + h = (h << 4) + *ptr++; |
| + unsigned g = h & 0xf0000000; |
| + h ^= g; |
| + h ^= g >> 24; |
| + } |
| + return h; |
| +} |
| + |
| +ELF::Sym* LookupSymbolForAddress(SharedLibrary* lib, void* address) { |
| + ELF::Addr elf_addr = reinterpret_cast<ELF::Addr>(address) - lib->base; |
| + |
| + for (size_t n = 0; n < lib->nchain; ++n) { |
| + ELF::Sym* sym = &lib->symtab[n]; |
| + if (sym->st_shndx != SHN_UNDEF && |
| + elf_addr >= sym->st_value && |
| + elf_addr < sym->st_value + sym->st_size) { |
| + return sym; |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| +// Call a constructor of destructor function pointer. Ignores |
|
Nico
2013/09/08 21:24:57
s/of/or/
digit1
2013/09/10 09:23:30
Done.
|
| +// NULL and -1 values intentionally. They correspond to markers |
| +// in the tables, or deleted values. |
| +// |func_type| corresponds to the type of the function, and is only |
| +// used for debugging (examples are "DT_INIT", "DT_INIT_ARRAY", etc...). |
| +void CallFunction(linker_function_t func, |
| + const char* func_type) { |
| + uintptr_t func_address = reinterpret_cast<uintptr_t>(func); |
| + |
| + LOG("%s: %p %s\n", __FUNCTION__, func, func_type); |
| + if (func_address != 0 && func_address != uintptr_t(-1)) |
| + func(); |
| +} |
| + |
| +// Parse the dynamic section of |lib| and populate various important |
| +// fields from it. On success, returns true. On failure, returns false, |
| +// and sets |error| message. |
| +bool ParseLibraryDynamicTable(SharedLibrary* lib, Error* error) { |
| + const ELF::Phdr* phdr = lib->phdr; |
| + size_t phdr_count = lib->phnum; |
| + ELF::Addr base = lib->base; |
| + |
| + phdr_table_get_dynamic_section(phdr, phdr_count, base, |
| + &lib->dynamic, |
| + &lib->dynamic_count, |
| + &lib->dynamic_flags); |
| + if (!lib->dynamic) { |
| + *error = "No PT_DYNAMIC section!"; |
| + return false; |
| + } |
| + |
| +#ifdef __arm__ |
| + (void) phdr_table_get_arm_exidx(phdr, phdr_count, base, |
| + &lib->ARM_exidx, &lib->ARM_exidx_count); |
| +#endif |
| + |
| + for (ELF::Dyn* dyn = lib->dynamic; dyn->d_tag != DT_NULL; ++dyn) { |
| + ELF::Addr dyn_value = dyn->d_un.d_val; |
| + uintptr_t dyn_addr = base + dyn->d_un.d_ptr; |
| + switch (dyn->d_tag) { |
| + case DT_HASH: |
| + LOG(" DT_HASH addr=%p\n", dyn_addr); |
| + { |
| + uintptr_t* data = reinterpret_cast<uintptr_t*>(dyn_addr); |
| + lib->nbucket = data[0]; |
| + lib->nchain = data[1]; |
| + lib->bucket = data + 2; |
| + lib->chain = data + 2 + lib->nbucket; |
| + } |
| + break; |
| + case DT_STRTAB: |
| + LOG(" DT_STRTAB addr=%p\n", dyn_addr); |
| + lib->strtab = reinterpret_cast<const char*>(dyn_addr); |
| + break; |
| + case DT_SYMTAB: |
| + LOG(" DT_SYMTAB addr=%p\n", dyn_addr); |
| + lib->symtab = reinterpret_cast<ELF::Sym*>(dyn_addr); |
| + break; |
| + case DT_DEBUG: |
| + // TODO(digit): Move this to a different location. |
| + if (lib->dynamic_flags & PF_W) { |
| + dyn->d_un.d_val = reinterpret_cast<uintptr_t>( |
| + Globals::GetRDebug()->GetAddress()); |
| + } |
| + break; |
| + case DT_INIT: |
| + LOG(" DT_INIT addr=%p\n", dyn_addr); |
| + lib->init_func = reinterpret_cast<linker_function_t>(dyn_addr); |
| + break; |
| + case DT_FINI: |
| + LOG(" DT_FINI addr=%p\n", dyn_addr); |
| + lib->fini_func = reinterpret_cast<linker_function_t>(dyn_addr); |
| + break; |
| + case DT_INIT_ARRAY: |
| + LOG(" DT_INIT_ARRAY addr=%p\n", dyn_addr); |
| + lib->init_array = reinterpret_cast<linker_function_t*>(dyn_addr); |
| + break; |
| + case DT_INIT_ARRAYSZ: |
| + lib->init_array_count = dyn_value / sizeof(ELF::Addr); |
| + LOG(" DT_INIT_ARRAYSZ value=%p count=%p\n", dyn_value, lib->init_array_count); |
| + break; |
| + case DT_FINI_ARRAY: |
| + LOG(" DT_FINI_ARRAY addr=%p\n", dyn_addr); |
| + lib->fini_array = reinterpret_cast<linker_function_t*>(dyn_addr); |
| + break; |
| + case DT_FINI_ARRAYSZ: |
| + lib->fini_array_count = dyn_value / sizeof(ELF::Addr); |
| + LOG(" DT_FINI_ARRAYSZ value=%p count=%p\n", dyn_value, lib->fini_array_count); |
| + break; |
| + case DT_PREINIT_ARRAY: |
| + LOG(" DT_PREINIT_ARRAY addr=%p\n", dyn_addr); |
| + lib->preinit_array = reinterpret_cast<linker_function_t*>(dyn_addr); |
| + break; |
| + case DT_PREINIT_ARRAYSZ: |
| + lib->preinit_array_count = dyn_value / sizeof(ELF::Addr); |
| + LOG(" DT_PREINIT_ARRAYSZ value=%p count=%p\n", dyn_value, lib->preinit_array_count); |
| + break; |
| + case DT_SYMBOLIC: |
| + LOG(" DT_SYMBOLIC\n"); |
| + lib->has_DT_SYMBOLIC = true; |
| + break; |
| + case DT_FLAGS: |
| + if (dyn_value & DF_SYMBOLIC) |
| + lib->has_DT_SYMBOLIC = true; |
| + break; |
| +#if defined(__mips__) |
| + case DT_MIPS_RLD_MAP: |
| + // TODO(digit): Move this to different location. |
| + dyn->d_un.d_ptr = |
| + reinterpret_cast<ELF::Addr>(Globals::GetRDebug()->GetAddress()); |
| + break; |
| +#endif |
| + default: |
| + ; |
| + } |
| + } |
| + |
| + // Perform a few sanity checks. |
| + if (!lib->nbucket) { |
| + *error = "Missing DT_HASH entry (built with --hash-style=gnu?)"; |
| + return false; |
| + } |
| + if (!lib->strtab) { |
| + *error = "Missing DT_STRTAB entry"; |
| + return false; |
| + } |
| + if (!lib->symtab) { |
| + *error = "Missing DT_SYMTAB entry"; |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +bool LoadLibrary(SharedLibrary* lib, |
| + const char* full_path, |
| + size_t load_address, |
| + size_t file_offset, |
| + Error* error) { |
| + // First, record the path. |
| + LOG("%s: full path '%s'\n", __FUNCTION__, full_path); |
| + |
| + size_t full_path_len = strlen(full_path); |
| + if (full_path_len >= sizeof(lib->full_path)) { |
| + error->Format("Path too long: %s", full_path); |
| + return false; |
| + } |
| + |
| + strlcpy(lib->full_path, full_path, sizeof(lib->full_path)); |
| + lib->base_name = GetBaseNamePtr(lib->full_path); |
| + |
| + // Load the ELF binary in memory. |
| + LOG("%s: Loading ELF segments for %s\n", __FUNCTION__, lib->base_name); |
| + |
| + { |
| + ElfLoader loader; |
| + if (!loader.LoadAt(lib->full_path, |
| + file_offset, |
| + load_address, |
| + error)) { |
| + return false; |
| + } |
| + |
| + lib->base = loader.load_start(); |
| + lib->size = loader.load_size(); |
| + lib->load_bias = loader.load_bias(); |
| + lib->phnum = loader.phdr_count(); |
| + lib->phdr = loader.loaded_phdr(); |
| + } |
| + |
| + // Parse the dynamic table to extract useful information. |
| + LOG("%s: Parsing dynamic table of %s\n", __FUNCTION__, lib->base_name); |
| + if (!ParseLibraryDynamicTable(lib, error)) |
| + return false; |
| + |
| + LOG("%s: Load complete for %s\n", __FUNCTION__, lib->base_name); |
| + return true; |
| +} |
| + |
| +// An instance of ElfRelocator::SymbolResolver that can be used |
| +// to resolve symbols in a shared library being loaded by |
| +// LibraryList::LoadLibrary. |
| +class SharedLibraryResolver: public ElfRelocator::SymbolResolver { |
| +public: |
| + SharedLibraryResolver(SharedLibrary* lib, |
| + LibraryList* lib_list, |
| + Vector<LibraryView*>* dependencies) |
| + : lib_(lib), |
| + lib_list_(lib_list), |
| + dependencies_(dependencies) {} |
| + |
| + virtual void* Lookup(const char* symbol_name) { |
| + // TODO(digit): Add the ability to lookup inside the main executable. |
| + |
| + // First, look inside the current library. |
| + ELF::Sym* entry = lib_->LookupSymbolEntry(symbol_name); |
| + if (entry) |
| + return reinterpret_cast<void*>(lib_->load_bias + entry->st_value); |
| + |
| + // Special case: redirect the dynamic linker symbols to our wrappers. |
| + // This ensures that loaded libraries can call dlopen() / dlsym() |
| + // and transparently use the crazy linker to perform their duty. |
| + void* address = WrapLinkerSymbol(symbol_name); |
| + if (address) |
| + return address; |
| + |
| + // Then look inside the dependencies. |
| + for (size_t n = 0; n < dependencies_->GetCount(); ++n) { |
| + LibraryView* wrap = (*dependencies_)[n]; |
| + //LOG("%s: Looking into dependency %p (%s)\n", __FUNCTION__, wrap, wrap->GetName()); |
| + if (wrap->IsSystem()) { |
| + address = ::dlsym(wrap->GetSystem(), symbol_name); |
| + if (address) |
| + return address; |
| + } |
| + if (wrap->IsCrazy()) { |
| + SharedLibrary* dep = wrap->GetCrazy(); |
| + entry = dep->LookupSymbolEntry(symbol_name); |
| + if (entry) |
| + return reinterpret_cast<void*>(dep->load_bias + entry->st_value); |
| + } |
| + } |
| + |
| + // Nothing found here. |
| + return NULL; |
| + } |
| + |
| +private: |
| + SharedLibrary* lib_; |
| + LibraryList* lib_list_; |
| + Vector<LibraryView*>* dependencies_; |
| +}; |
| + |
| +bool RelocateLibrary(SharedLibrary* lib, |
| + LibraryList* lib_list, |
| + Vector<LibraryView*>* dependencies, |
| + Error* error) { |
| + // Apply relocations. |
| + LOG("%s: Applying relocations to %s\n", __FUNCTION__, lib->base_name); |
| + |
| + ElfRelocator relocator; |
| + |
| + if (!relocator.Init(lib->phdr, |
| + lib->phnum, |
| + lib->load_bias, |
| + lib->dynamic, |
| + lib->dynamic_count, |
| + error)) { |
| + return false; |
| + } |
| + |
| + SharedLibraryResolver resolver(lib, lib_list, dependencies); |
| + if (!relocator.Apply(&resolver, lib->strtab, lib->symtab, error)) |
| + return false; |
| + |
| + LOG("%s: Relocations applied for %s\n", __FUNCTION__, lib->base_name); |
| + return true; |
| +} |
| + |
| +// Helper class for a memory mapping. This is _not_ scoped. |
| +class Mapping { |
| +public: |
| + enum Protection { |
| + CAN_READ = PROT_READ, |
| + CAN_WRITE = PROT_WRITE, |
| + CAN_READ_WRITE = PROT_READ|PROT_WRITE |
| + }; |
| + Mapping() : map_(NULL), size_(0) {} |
| + ~Mapping() {} |
| + |
| + // Return current mapping address. |
| + void* Get() { return map_; } |
| + |
| + // Allocate a new mapping. |
| + // |address| is either NULL or a fixed memory address. |
| + // |size| is the page-aligned size, must be > 0. |
| + // |prot| are the desired protection bit flags. |
| + // |fd| is -1 (for anonymous mappings), or a valid file descriptor. |
| + // on failure, return false and sets |error| message. |
| + bool Allocate(void* address, |
| + size_t size, |
| + Protection prot, |
| + int fd, |
| + Error* error) { |
| + int flags = (fd >= 0) ? MAP_SHARED : MAP_ANONYMOUS; |
| + if (address) |
| + flags |= MAP_FIXED; |
| + |
| + size_ = size; |
| + map_ = ::mmap(address, size_, static_cast<int>(prot), flags, fd, 0); |
| + if (map_ == MAP_FAILED) { |
| + error->Format("Cannot map %d bytes (addr=%p, fd=%d): %s", |
| + size, address, fd, strerror(errno)); |
| + map_ = NULL; |
| + return false; |
| + } |
| + |
| + return true; |
| + } |
| + |
| + // Change the protection flags of the mapping. |
| + bool SetProtection(Protection prot, Error* error) { |
| + if (map_) { |
| + if (::mprotect(map_, size_, static_cast<int>(prot)) < 0) { |
| + char* p = reinterpret_cast<char*>(map_); |
| + error->Format("Cannot change protection for %p-%p: %s", |
| + p, p + size_, strerror(errno)); |
| + return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + // Deallocate an existing mapping, if any. |
| + void Deallocate() { |
| + if (map_) { |
| + ::munmap(map_, size_); |
| + map_ = NULL; |
| + } |
| + } |
| + |
| +protected: |
| + void* map_; |
| + size_t size_; |
| +}; |
| + |
| +// Helper class for a memory mapping that is automatically |
| +// unmapped on scope exit, unless its Release() method is called. |
| +class ScopedMapping : public Mapping { |
| +public: |
| + void* Release() { |
| + void* ret = map_; |
| + map_ = NULL; |
| + return ret; |
| + } |
| + |
| + ~ScopedMapping() { |
| + Deallocate(); |
| + } |
| +}; |
| + |
| +// Copy all the pages from |addr| and |addr + size| into an ashmem |
| +// region identified by |fd|. |
| +bool CopyPagesToFd(void* addr, size_t size, int fd, Error* error) { |
| + |
| + // Create temporary mapping of the ashmem region. |
| + // And copy current pages there. |
| + ScopedMapping fd_map; |
| + |
| + if (!fd_map.Allocate(NULL, |
| + size, |
| + ScopedMapping::CAN_READ_WRITE, |
| + fd, |
| + error)) { |
| + return false; |
| + } |
| + |
| + // Copy current pages there. |
| + ::memcpy(fd_map.Get(), addr, size); |
| + return true; |
| +} |
| + |
| +// Swap pages between |addr| and |addr + size| with the bytes |
| +// from the ashmem region identified by |fd|, starting from |
| +// a given |offset|. On failure return false and set |error| message. |
| +bool SwapPagesFromFd(void* addr, |
| + size_t size, |
| + int fd, |
| + size_t offset, |
| + Error* error) { |
| + // Unmap current pages. |
| + if (::munmap(addr, size) < 0) { |
| + error->Format("%s: Could not unmap %p-%p: %s", |
| + __FUNCTION__, addr, (char*)addr + size, |
| + strerror(errno)); |
| + return false; |
| + } |
| + |
| + // Remap the fd pages at the same location now. |
| + void* new_map = ::mmap(addr, size, PROT_READ, MAP_FIXED|MAP_SHARED, fd, |
| + static_cast<off_t>(offset)); |
| + if (new_map == MAP_FAILED) { |
| + char* p = reinterpret_cast<char*>(addr); |
| + error->Format("%s: Could not map %p-%p: %s", |
| + __FUNCTION__, p, p + size, strerror(errno)); |
| + return false; |
| + } |
| + |
| +#ifdef __arm__ |
| + __clear_cache(addr, (char*)addr + size); |
| +#endif |
| + |
| + // Done. |
| + return true; |
| +} |
| + |
| +bool PageEquals(const char* p1, const char* p2) { |
| + // TODO(digit): For some reason, using memcmp() here crashes on Android 4.3. |
|
Nico
2013/09/08 21:24:57
Weird.
digit1
2013/09/10 09:23:30
Indeed :-(
|
| + for (size_t n = 0; n < PAGE_SIZE; ++n) { |
| + if (p1[n] != p2[n]) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +// Conditionally swap pages between |address| and |address + size| with |
| +// the ones from the ashmem region identified by |fd|. This only swaps |
| +// pages that have exactly the same content. On failure, return false |
| +// and set |error| message. |
| +bool SwapSimilarPagesFromFd(void* addr, size_t size, int fd, Error* error) { |
| + // Create temporary mapping of the ashmem region. |
| + ScopedMapping fd_map; |
| + |
| + LOG("%s: Entering addr=%p size=%p fd=%d\n", __FUNCTION__, |
| + addr, (void*)size, fd); |
| + |
| + if (!fd_map.Allocate(NULL, size, ScopedMapping::CAN_READ, fd, error)) |
| + return false; |
| + |
| + LOG("%s: mapping allocate at %p\n", __FUNCTION__, fd_map.Get()); |
| + |
| + char* cur_page = reinterpret_cast<char*>(addr); |
| + char* fd_page = reinterpret_cast<char*>(fd_map.Get()); |
| + size_t p = 0; |
| + size_t similar_size = 0; |
| + |
| + do { |
| + // Skip over dissimilar pages. |
| + LOG("%s: test pos=%d\n", __FUNCTION__, (int)p); |
| + while (p < size && !PageEquals(cur_page + p, fd_page + p)) { |
| + LOG("%s: Skipped over page at %p\n", __FUNCTION__, cur_page + p); |
| + p += PAGE_SIZE; |
| + } |
| + |
| + // Count similar pages. |
| + size_t p2 = p; |
| + while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) { |
| + LOG("%s: Found similar page at %p\n", __FUNCTION__, cur_page + p2); |
| + p2 += PAGE_SIZE; |
| + } |
| + |
| + if (p2 > p) { |
| + // Swap pages between |pos| and |pos2|. |
| + LOG("%s: Swap pages at %p-%p\n", __FUNCTION__, |
| + cur_page + p, cur_page + p2); |
| + if (!SwapPagesFromFd(cur_page + p, p2 - p, fd, p, error)) |
| + return false; |
| + |
| + similar_size += (p2 - p); |
| + } |
| + |
| + p = p2; |
| + } while (p < size); |
| + |
| + LOG("%s: Swapped %d pages over %d (%d %%, %d KB)\n", |
| + __FUNCTION__, |
| + similar_size / PAGE_SIZE, |
| + size / PAGE_SIZE, |
| + similar_size * 100 / size, |
| + similar_size / 4096); |
| + |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +SharedLibrary::SharedLibrary() { |
| + // Ensure all fields are empty. |
| + memset(this, 0, sizeof(*this)); |
| + this->relro_fd = -1; |
| +} |
| + |
| +SharedLibrary::~SharedLibrary() { |
| + // Ensure the library is unmapped on destruction. |
| + if (this->base) |
| + munmap(reinterpret_cast<void*>(this->base), this->size); |
| + // Ensure the relro ashmem file descriptor is closed. |
| + if (this->relro_fd >= 0) |
| + close(this->relro_fd); |
| +} |
| + |
| +bool SharedLibrary::Load(const char* full_path, |
| + size_t load_address, |
| + size_t file_offset, |
| + Error* error) { |
| + return LoadLibrary(this, full_path, load_address, file_offset, error); |
| +} |
| + |
| +bool SharedLibrary::Relocate(LibraryList* lib_list, |
| + Vector<LibraryView*>* dependencies, |
| + Error* error) { |
| + return RelocateLibrary(this, lib_list, dependencies, error); |
| +} |
| + |
| +ELF::Sym* SharedLibrary::LookupSymbolEntry(const char* symbol_name) { |
| + unsigned hash = ElfHash(symbol_name); |
| + ELF::Sym* symbols = this->symtab; |
| + const char* strings = this->strtab; |
| + |
| + for (unsigned n = this->bucket[hash % this->nbucket]; |
| + n != 0; |
| + n = this->chain[n]) { |
| + ELF::Sym* symbol = &symbols[n]; |
| + // Check that the symbol has the appropriate name. |
| + if (strcmp(strings + symbol->st_name, symbol_name)) |
| + continue; |
| + // Ignore undefined symbols. |
| + if (symbol->st_shndx == SHN_UNDEF) |
| + continue; |
| + // Ignore anything that isn't a global or weak definition. |
| + switch (ELF_ST_BIND(symbol->st_info)) { |
| + case STB_GLOBAL: |
| + case STB_WEAK: |
| + return symbol; |
| + default: |
| + ; |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| +void* SharedLibrary::FindAddressForSymbol(const char* symbol_name) { |
| + ELF::Sym* entry = LookupSymbolEntry(symbol_name); |
| + if (!entry) |
| + return NULL; |
| + |
| + return reinterpret_cast<void*>(this->load_bias + entry->st_value); |
| +} |
| + |
| +ELF::Sym* SharedLibrary::FindSymbolForAddress(void* address) { |
| + return LookupSymbolForAddress(this, address); |
| +} |
| + |
| +bool SharedLibrary::EnableSharedRelro(Error* error) { |
| + if (this->relro_fd != -1) |
| + return true; |
| + |
| + ELF::Addr relro_start, relro_size; |
| + if (phdr_table_get_relro_info(this->phdr, this->phnum, this->load_bias, |
| + &relro_start, &relro_size) < 0) { |
| + // No RELRO section to share. |
| + return true; |
| + } |
| + |
| + if (relro_size == 0) { |
| + // Probably means there is no real RELRO section. |
| + return true; |
| + } |
| + |
| + // Allocate new ashmem region. |
| + this->relro_start = relro_start; |
| + this->relro_size = relro_size; |
| + AshmemRegion ashmem; |
| + String relro_name("RELRO:"); |
| + relro_name += this->base_name; |
| + if (!ashmem.Allocate(relro_size, relro_name.c_str())) { |
| + error->Format("Could not allocate ashmem region: %s", strerror(errno)); |
| + return false; |
| + } |
| + |
| + void* relro_addr = reinterpret_cast<void*>(relro_start); |
| + if (!CopyPagesToFd(relro_addr, relro_size, ashmem.Get(), error) || |
| + !SwapPagesFromFd(relro_addr, relro_size, ashmem.Get(), 0, error)) |
| + return false; |
| + |
| + this->relro_fd = ashmem.Release(); |
| + return true; |
| +} |
| + |
| +bool SharedLibrary::UseSharedRelro(size_t relro_start, |
| + size_t relro_size, |
|
Nico
2013/09/08 21:24:57
(nit: indent)
digit1
2013/09/10 09:23:30
Done.
|
| + int relro_fd, |
| + Error* error) { |
| + if (relro_fd < 0 || relro_size == 0) { |
| + // Nothing to do here. |
| + return true; |
| + } |
| + |
| + LOG("%s: relro_start=%p relro_size=%p relro_fd=%d\n", |
| + __FUNCTION__, (void*)relro_start, (void*)relro_size, relro_fd); |
| + |
| + // Sanity checks. |
| + if (this->relro_fd != -1) { |
| + *error = "Library already using shared RELRO section"; |
| + return false; |
| + } |
| + |
| + ELF::Addr lib_relro_start, lib_relro_size; |
| + if (phdr_table_get_relro_info(this->phdr, this->phnum, this->load_bias, |
| + &lib_relro_start, &lib_relro_size) < 0) { |
| + *error = "No RELRO section in library"; |
| + return false; |
| + } |
| + |
| + if (lib_relro_start != relro_start || lib_relro_size != relro_size) { |
| + error->Format("RELRO mismatch addr=%p size=%p (wanted addr=%p size=%p)", |
| + lib_relro_start, lib_relro_size, relro_start, relro_size); |
| + return false; |
| + } |
| + |
| + void* relro_addr = reinterpret_cast<void*>(relro_start); |
| + if (!SwapSimilarPagesFromFd(relro_addr, relro_size, relro_fd, error)) |
| + return false; |
| + |
| + this->relro_fd = relro_fd; |
| + return true; |
| +} |
| + |
| +void SharedLibrary::CallConstructors() { |
| + CallFunction(this->init_func, "DT_INIT"); |
| + for (size_t n = 0; n < this->init_array_count; ++n) |
| + CallFunction(this->init_array[n], "DT_INIT_ARRAY"); |
| +} |
| + |
| +void SharedLibrary::CallDestructors() { |
| + for (size_t n = this->fini_array_count; n > 0; --n) { |
| + CallFunction(this->fini_array[n - 1], "DT_FINI_ARRAY"); |
| + } |
| + CallFunction(this->fini_func, "DT_FINI"); |
| +} |
| + |
| +} // namespace crazy |