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 |