Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relro.cpp |
diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relro.cpp b/third_party/crazy_linker/crazy_linker/src/crazy_linker_elf_relro.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1c0bf00a8d80359fe6e5595cd18bc88d33074abc |
--- /dev/null |
+++ b/third_party/crazy_linker/crazy_linker/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 |