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.
5 #include "crazy_linker_elf_relro.h"
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"
20 inline bool PageEquals(const char* p1, const char* p2) {
21 return ::memcmp(p1, p2, PAGE_SIZE) == 0;
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.
27 bool SwapPagesFromFd(void* addr,
32 // Unmap current pages.
33 if (::munmap(addr, size) < 0) {
34 error->Format("%s: Could not unmap %p-%p: %s",
42 // Remap the fd pages at the same location now.
43 void* new_map = ::mmap(addr,
46 MAP_FIXED | MAP_SHARED,
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",
59 // TODO(digit): Is this necessary?
61 __clear_cache(addr, (char*)addr + size);
70 bool SharedRelro::Allocate(size_t relro_size,
71 const char* library_name,
73 // Allocate a new ashmem region.
74 String name("RELRO:");
76 if (!ashmem_.Allocate(relro_size, name.c_str())) {
77 error->Format("Could not allocate RELRO ashmem region for %s: %s",
88 bool SharedRelro::CopyFrom(size_t relro_start,
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));
98 // Copy process' RELRO into it.
99 ::memcpy(map.Get(), reinterpret_cast<void*>(relro_start), relro_size);
104 // Everything's good.
105 start_ = relro_start;
110 bool SharedRelro::CopyFromRelocated(const ElfView* view,
115 // Offset of RELRO section in current library.
116 size_t relro_offset = relro_start - view->load_address();
118 ElfRelocations relocations;
119 if (!relocations.Init(view, error))
122 // Map the region in memory (any address).
123 ScopedMemoryMapping map;
125 NULL, relro_size, MemoryMapping::CAN_READ_WRITE, ashmem_.fd())) {
126 error->Format("Could not allocate RELRO mapping for: %s", strerror(errno));
130 // Copy and relocate.
131 relocations.CopyAndRelocate(relro_start,
132 reinterpret_cast<size_t>(map.Get()),
133 load_address + relro_offset,
137 start_ = load_address + relro_offset;
142 bool 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",
152 bool SharedRelro::InitFrom(size_t relro_start,
156 // Create temporary mapping of the ashmem region.
157 ScopedMemoryMapping fd_map;
159 LOG("%s: Entering addr=%p size=%p fd=%d\n",
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",
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",
178 LOG("%s: mapping allocated at %p\n", __FUNCTION__, fd_map.Get());
180 char* cur_page = reinterpret_cast<char*>(relro_start);
181 char* fd_page = static_cast<char*>(fd_map.Get());
183 size_t size = relro_size;
184 size_t similar_size = 0;
187 // Skip over dissimilar pages.
188 while (p < size && !PageEquals(cur_page + p, fd_page + p)) {
192 // Count similar pages.
194 while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) {
199 // Swap pages between |pos| and |pos2|.
200 LOG("%s: Swap pages at %p-%p\n",
204 if (!SwapPagesFromFd(cur_page + p, p2 - p, ashmem_fd, p, error))
207 similar_size += (p2 - p);
213 LOG("%s: Swapped %d pages over %d (%d %%, %d KB not shared)\n",
215 similar_size / PAGE_SIZE,
217 similar_size * 100 / size,
218 (size - similar_size) / 4096);
220 if (similar_size == 0) {
221 error->Format("No pages were swapped into RELRO ashmem");
225 start_ = relro_start;