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