crazy_linker_elf_relro.cpp revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "crazy_linker_elf_relro.h" 6 7#include <errno.h> 8#include <limits.h> 9#include <stdlib.h> 10 11#include "crazy_linker_elf_relocations.h" 12#include "crazy_linker_elf_view.h" 13#include "crazy_linker_memory_mapping.h" 14#include "crazy_linker_util.h" 15 16namespace crazy { 17 18namespace { 19 20inline bool PageEquals(const char* p1, const char* p2) { 21 return ::memcmp(p1, p2, PAGE_SIZE) == 0; 22} 23 24// Swap pages between |addr| and |addr + size| with the bytes 25// from the ashmem region identified by |fd|, starting from 26// a given |offset|. On failure return false and set |error| message. 27bool SwapPagesFromFd(void* addr, 28 size_t size, 29 int fd, 30 size_t offset, 31 Error* error) { 32 // Unmap current pages. 33 if (::munmap(addr, size) < 0) { 34 error->Format("%s: Could not unmap %p-%p: %s", 35 __FUNCTION__, 36 addr, 37 (char*)addr + size, 38 strerror(errno)); 39 return false; 40 } 41 42 // Remap the fd pages at the same location now. 43 void* new_map = ::mmap(addr, 44 size, 45 PROT_READ, 46 MAP_FIXED | MAP_SHARED, 47 fd, 48 static_cast<off_t>(offset)); 49 if (new_map == MAP_FAILED) { 50 char* p = reinterpret_cast<char*>(addr); 51 error->Format("%s: Could not map %p-%p: %s", 52 __FUNCTION__, 53 p, 54 p + size, 55 strerror(errno)); 56 return false; 57 } 58 59// TODO(digit): Is this necessary? 60#ifdef __arm__ 61 __clear_cache(addr, (char*)addr + size); 62#endif 63 64 // Done. 65 return true; 66} 67 68} // namespace 69 70bool SharedRelro::Allocate(size_t relro_size, 71 const char* library_name, 72 Error* error) { 73 // Allocate a new ashmem region. 74 String name("RELRO:"); 75 name += library_name; 76 if (!ashmem_.Allocate(relro_size, name.c_str())) { 77 error->Format("Could not allocate RELRO ashmem region for %s: %s", 78 library_name, 79 strerror(errno)); 80 return false; 81 } 82 83 start_ = 0; 84 size_ = relro_size; 85 return true; 86} 87 88bool SharedRelro::CopyFrom(size_t relro_start, 89 size_t relro_size, 90 Error* error) { 91 // Map it in the process. 92 ScopedMemoryMapping map; 93 if (!map.Allocate(NULL, relro_size, MemoryMapping::CAN_WRITE, ashmem_.fd())) { 94 error->Format("Could not allocate RELRO mapping: %s", strerror(errno)); 95 return false; 96 } 97 98 // Copy process' RELRO into it. 99 ::memcpy(map.Get(), reinterpret_cast<void*>(relro_start), relro_size); 100 101 // Unmap it. 102 map.Deallocate(); 103 104 // Everything's good. 105 start_ = relro_start; 106 size_ = relro_size; 107 return true; 108} 109 110bool SharedRelro::CopyFromRelocated(const ElfView* view, 111 size_t load_address, 112 size_t relro_start, 113 size_t relro_size, 114 Error* error) { 115 // Offset of RELRO section in current library. 116 size_t relro_offset = relro_start - view->load_address(); 117 118 ElfRelocations relocations; 119 if (!relocations.Init(view, error)) 120 return false; 121 122 // Map the region in memory (any address). 123 ScopedMemoryMapping map; 124 if (!map.Allocate( 125 NULL, relro_size, MemoryMapping::CAN_READ_WRITE, ashmem_.fd())) { 126 error->Format("Could not allocate RELRO mapping for: %s", strerror(errno)); 127 return false; 128 } 129 130 // Copy and relocate. 131 relocations.CopyAndRelocate(relro_start, 132 reinterpret_cast<size_t>(map.Get()), 133 load_address + relro_offset, 134 relro_size); 135 // Unmap it. 136 map.Deallocate(); 137 start_ = load_address + relro_offset; 138 size_ = relro_size; 139 return true; 140} 141 142bool SharedRelro::ForceReadOnly(Error* error) { 143 // Ensure the ashmem region content isn't writable anymore. 144 if (!ashmem_.SetProtectionFlags(PROT_READ)) { 145 error->Format("Could not make RELRO ashmem region read-only: %s", 146 strerror(errno)); 147 return false; 148 } 149 return true; 150} 151 152bool SharedRelro::InitFrom(size_t relro_start, 153 size_t relro_size, 154 int ashmem_fd, 155 Error* error) { 156 // Create temporary mapping of the ashmem region. 157 ScopedMemoryMapping fd_map; 158 159 LOG("%s: Entering addr=%p size=%p fd=%d\n", 160 __FUNCTION__, 161 (void*)relro_start, 162 (void*)relro_size, 163 ashmem_fd); 164 165 // Sanity check: Ashmem file descriptor must be read-only. 166 if (!AshmemRegion::CheckFileDescriptorIsReadOnly(ashmem_fd)) { 167 error->Format("Ashmem file descriptor is not read-only: %s\n", 168 strerror(errno)); 169 return false; 170 } 171 172 if (!fd_map.Allocate(NULL, relro_size, MemoryMapping::CAN_READ, ashmem_fd)) { 173 error->Format("Cannot map RELRO ashmem region as read-only: %s\n", 174 strerror(errno)); 175 return false; 176 } 177 178 LOG("%s: mapping allocated at %p\n", __FUNCTION__, fd_map.Get()); 179 180 char* cur_page = reinterpret_cast<char*>(relro_start); 181 char* fd_page = static_cast<char*>(fd_map.Get()); 182 size_t p = 0; 183 size_t size = relro_size; 184 size_t similar_size = 0; 185 186 do { 187 // Skip over dissimilar pages. 188 while (p < size && !PageEquals(cur_page + p, fd_page + p)) { 189 p += PAGE_SIZE; 190 } 191 192 // Count similar pages. 193 size_t p2 = p; 194 while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) { 195 p2 += PAGE_SIZE; 196 } 197 198 if (p2 > p) { 199 // Swap pages between |pos| and |pos2|. 200 LOG("%s: Swap pages at %p-%p\n", 201 __FUNCTION__, 202 cur_page + p, 203 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