| Index: third_party/android_crazy_linker/src/src/crazy_linker_elf_relro.cpp
|
| diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relro.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relro.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1c0bf00a8d80359fe6e5595cd18bc88d33074abc
|
| --- /dev/null
|
| +++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relro.cpp
|
| @@ -0,0 +1,228 @@
|
| +// 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_elf_relro.h"
|
| +
|
| +#include <errno.h>
|
| +#include <limits.h>
|
| +#include <stdlib.h>
|
| +
|
| +#include "crazy_linker_elf_relocations.h"
|
| +#include "crazy_linker_elf_view.h"
|
| +#include "crazy_linker_memory_mapping.h"
|
| +#include "crazy_linker_util.h"
|
| +
|
| +namespace crazy {
|
| +
|
| +namespace {
|
| +
|
| +inline bool PageEquals(const char* p1, const char* p2) {
|
| + return ::memcmp(p1, p2, PAGE_SIZE) == 0;
|
| +}
|
| +
|
| +// 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;
|
| + }
|
| +
|
| +// TODO(digit): Is this necessary?
|
| +#ifdef __arm__
|
| + __clear_cache(addr, (char*)addr + size);
|
| +#endif
|
| +
|
| + // Done.
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +bool SharedRelro::Allocate(size_t relro_size,
|
| + const char* library_name,
|
| + Error* error) {
|
| + // Allocate a new ashmem region.
|
| + String name("RELRO:");
|
| + name += library_name;
|
| + if (!ashmem_.Allocate(relro_size, name.c_str())) {
|
| + error->Format("Could not allocate RELRO ashmem region for %s: %s",
|
| + library_name,
|
| + strerror(errno));
|
| + return false;
|
| + }
|
| +
|
| + start_ = 0;
|
| + size_ = relro_size;
|
| + return true;
|
| +}
|
| +
|
| +bool SharedRelro::CopyFrom(size_t relro_start,
|
| + size_t relro_size,
|
| + Error* error) {
|
| + // Map it in the process.
|
| + ScopedMemoryMapping map;
|
| + if (!map.Allocate(NULL, relro_size, MemoryMapping::CAN_WRITE, ashmem_.fd())) {
|
| + error->Format("Could not allocate RELRO mapping: %s", strerror(errno));
|
| + return false;
|
| + }
|
| +
|
| + // Copy process' RELRO into it.
|
| + ::memcpy(map.Get(), reinterpret_cast<void*>(relro_start), relro_size);
|
| +
|
| + // Unmap it.
|
| + map.Deallocate();
|
| +
|
| + // Everything's good.
|
| + start_ = relro_start;
|
| + size_ = relro_size;
|
| + return true;
|
| +}
|
| +
|
| +bool SharedRelro::CopyFromRelocated(const ElfView* view,
|
| + size_t load_address,
|
| + size_t relro_start,
|
| + size_t relro_size,
|
| + Error* error) {
|
| + // Offset of RELRO section in current library.
|
| + size_t relro_offset = relro_start - view->load_address();
|
| +
|
| + ElfRelocations relocations;
|
| + if (!relocations.Init(view, error))
|
| + return false;
|
| +
|
| + // Map the region in memory (any address).
|
| + ScopedMemoryMapping map;
|
| + if (!map.Allocate(
|
| + NULL, relro_size, MemoryMapping::CAN_READ_WRITE, ashmem_.fd())) {
|
| + error->Format("Could not allocate RELRO mapping for: %s", strerror(errno));
|
| + return false;
|
| + }
|
| +
|
| + // Copy and relocate.
|
| + relocations.CopyAndRelocate(relro_start,
|
| + reinterpret_cast<size_t>(map.Get()),
|
| + load_address + relro_offset,
|
| + relro_size);
|
| + // Unmap it.
|
| + map.Deallocate();
|
| + start_ = load_address + relro_offset;
|
| + size_ = relro_size;
|
| + return true;
|
| +}
|
| +
|
| +bool SharedRelro::ForceReadOnly(Error* error) {
|
| + // Ensure the ashmem region content isn't writable anymore.
|
| + if (!ashmem_.SetProtectionFlags(PROT_READ)) {
|
| + error->Format("Could not make RELRO ashmem region read-only: %s",
|
| + strerror(errno));
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool SharedRelro::InitFrom(size_t relro_start,
|
| + size_t relro_size,
|
| + int ashmem_fd,
|
| + Error* error) {
|
| + // Create temporary mapping of the ashmem region.
|
| + ScopedMemoryMapping fd_map;
|
| +
|
| + LOG("%s: Entering addr=%p size=%p fd=%d\n",
|
| + __FUNCTION__,
|
| + (void*)relro_start,
|
| + (void*)relro_size,
|
| + ashmem_fd);
|
| +
|
| + // Sanity check: Ashmem file descriptor must be read-only.
|
| + if (!AshmemRegion::CheckFileDescriptorIsReadOnly(ashmem_fd)) {
|
| + error->Format("Ashmem file descriptor is not read-only: %s\n",
|
| + strerror(errno));
|
| + return false;
|
| + }
|
| +
|
| + if (!fd_map.Allocate(NULL, relro_size, MemoryMapping::CAN_READ, ashmem_fd)) {
|
| + error->Format("Cannot map RELRO ashmem region as read-only: %s\n",
|
| + strerror(errno));
|
| + return false;
|
| + }
|
| +
|
| + LOG("%s: mapping allocated at %p\n", __FUNCTION__, fd_map.Get());
|
| +
|
| + char* cur_page = reinterpret_cast<char*>(relro_start);
|
| + char* fd_page = static_cast<char*>(fd_map.Get());
|
| + size_t p = 0;
|
| + size_t size = relro_size;
|
| + size_t similar_size = 0;
|
| +
|
| + do {
|
| + // Skip over dissimilar pages.
|
| + while (p < size && !PageEquals(cur_page + p, fd_page + p)) {
|
| + p += PAGE_SIZE;
|
| + }
|
| +
|
| + // Count similar pages.
|
| + size_t p2 = p;
|
| + while (p2 < size && PageEquals(cur_page + p2, fd_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, ashmem_fd, p, error))
|
| + return false;
|
| +
|
| + similar_size += (p2 - p);
|
| + }
|
| +
|
| + p = p2;
|
| + } while (p < size);
|
| +
|
| + LOG("%s: Swapped %d pages over %d (%d %%, %d KB not shared)\n",
|
| + __FUNCTION__,
|
| + similar_size / PAGE_SIZE,
|
| + size / PAGE_SIZE,
|
| + similar_size * 100 / size,
|
| + (size - similar_size) / 4096);
|
| +
|
| + if (similar_size == 0)
|
| + return false;
|
| +
|
| + start_ = relro_start;
|
| + size_ = relro_size;
|
| + return true;
|
| +}
|
| +
|
| +} // namespace crazy
|
|
|