15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crazy_linker_elf_relro.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <errno.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <limits.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crazy_linker_elf_relocations.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crazy_linker_elf_view.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crazy_linker_memory_mapping.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "crazy_linker_util.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace crazy {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)inline bool PageEquals(const char* p1, const char* p2) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ::memcmp(p1, p2, PAGE_SIZE) == 0;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Swap pages between |addr| and |addr + size| with the bytes
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// from the ashmem region identified by |fd|, starting from
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a given |offset|. On failure return false and set |error| message.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SwapPagesFromFd(void* addr,
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     size_t size,
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     int fd,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     size_t offset,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     Error* error) {
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Unmap current pages.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::munmap(addr, size) < 0) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("%s: Could not unmap %p-%p: %s",
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  __FUNCTION__,
36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                  addr,
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  (char*)addr + size,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  strerror(errno));
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
41c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
42c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Remap the fd pages at the same location now.
43c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  void* new_map = ::mmap(addr,
44c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         size,
45c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         PROT_READ,
46c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         MAP_FIXED | MAP_SHARED,
47c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         fd,
48c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         static_cast<off_t>(offset));
49c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (new_map == MAP_FAILED) {
50c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    char* p = reinterpret_cast<char*>(addr);
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("%s: Could not map %p-%p: %s",
52c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                  __FUNCTION__,
53c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                  p,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  p + size,
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  strerror(errno));
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(digit): Is this necessary?
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef __arm__
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  __clear_cache(addr, (char*)addr + size);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Done.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SharedRelro::Allocate(size_t relro_size,
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const char* library_name,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           Error* error) {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Allocate a new ashmem region.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  String name("RELRO:");
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  name += library_name;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ashmem_.Allocate(relro_size, name.c_str())) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("Could not allocate RELRO ashmem region for %s: %s",
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  library_name,
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  strerror(errno));
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  start_ = 0;
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  size_ = relro_size;
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return true;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool SharedRelro::CopyFrom(size_t relro_start,
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           size_t relro_size,
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           Error* error) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Map it in the process.
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedMemoryMapping map;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!map.Allocate(NULL, relro_size, MemoryMapping::CAN_WRITE, ashmem_.fd())) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("Could not allocate RELRO mapping: %s", strerror(errno));
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Copy process' RELRO into it.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::memcpy(map.Get(), reinterpret_cast<void*>(relro_start), relro_size);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Unmap it.
102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  map.Deallocate();
103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Everything's good.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  start_ = relro_start;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_ = relro_size;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SharedRelro::CopyFromRelocated(const ElfView* view,
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    size_t load_address,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    size_t relro_start,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    size_t relro_size,
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    Error* error) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Offset of RELRO section in current library.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t relro_offset = relro_start - view->load_address();
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ElfRelocations relocations;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!relocations.Init(view, error))
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Map the region in memory (any address).
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedMemoryMapping map;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!map.Allocate(
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           NULL, relro_size, MemoryMapping::CAN_READ_WRITE, ashmem_.fd())) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("Could not allocate RELRO mapping for: %s", strerror(errno));
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Copy and relocate.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  relocations.CopyAndRelocate(relro_start,
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              reinterpret_cast<size_t>(map.Get()),
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              load_address + relro_offset,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              relro_size);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Unmap it.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  map.Deallocate();
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  start_ = load_address + relro_offset;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_ = relro_size;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SharedRelro::ForceReadOnly(Error* error) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ensure the ashmem region content isn't writable anymore.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ashmem_.SetProtectionFlags(PROT_READ)) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("Could not make RELRO ashmem region read-only: %s",
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  strerror(errno));
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SharedRelro::InitFrom(size_t relro_start,
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           size_t relro_size,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           int ashmem_fd,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           Error* error) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create temporary mapping of the ashmem region.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedMemoryMapping fd_map;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LOG("%s: Entering addr=%p size=%p fd=%d\n",
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      __FUNCTION__,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (void*)relro_start,
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (void*)relro_size,
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ashmem_fd);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sanity check: Ashmem file descriptor must be read-only.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!AshmemRegion::CheckFileDescriptorIsReadOnly(ashmem_fd)) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("Ashmem file descriptor is not read-only: %s\n",
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  strerror(errno));
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!fd_map.Allocate(NULL, relro_size, MemoryMapping::CAN_READ, ashmem_fd)) {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error->Format("Cannot map RELRO ashmem region as read-only: %s\n",
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  strerror(errno));
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LOG("%s: mapping allocated at %p\n", __FUNCTION__, fd_map.Get());
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char* cur_page = reinterpret_cast<char*>(relro_start);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char* fd_page = static_cast<char*>(fd_map.Get());
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t p = 0;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t size = relro_size;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t similar_size = 0;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Skip over dissimilar pages.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (p < size && !PageEquals(cur_page + p, fd_page + p)) {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      p += PAGE_SIZE;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Count similar pages.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t p2 = p;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      p2 += PAGE_SIZE;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (p2 > p) {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Swap pages between |pos| and |pos2|.
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG("%s: Swap pages at %p-%p\n",
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          __FUNCTION__,
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cur_page + p,
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cur_page + p2);
204      if (!SwapPagesFromFd(cur_page + p, p2 - p, ashmem_fd, p, error))
205        return false;
206
207      similar_size += (p2 - p);
208    }
209
210    p = p2;
211  } while (p < size);
212
213  LOG("%s: Swapped %d pages over %d (%d %%, %d KB not shared)\n",
214      __FUNCTION__,
215      similar_size / PAGE_SIZE,
216      size / PAGE_SIZE,
217      similar_size * 100 / size,
218      (size - similar_size) / 4096);
219
220  if (similar_size == 0)
221    return false;
222
223  start_ = relro_start;
224  size_ = relro_size;
225  return true;
226}
227
228}  // namespace crazy
229