SharedMemoryPlatform_mach.cpp (7317B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "SharedMemoryPlatform.h" 8 9 #include <utility> 10 11 #include <mach/vm_map.h> 12 #include <mach/mach_port.h> 13 #if defined(XP_IOS) 14 # include <mach/vm_map.h> 15 # define mach_vm_address_t vm_address_t 16 # define mach_vm_map vm_map 17 # define mach_vm_read vm_read 18 # define mach_vm_region_recurse vm_region_recurse_64 19 # define mach_vm_size_t vm_size_t 20 #else 21 # include <mach/mach_vm.h> 22 #endif 23 #include <pthread.h> 24 #include <sys/mman.h> // mprotect 25 #include <unistd.h> 26 27 #if defined(XP_MACOSX) && defined(__x86_64__) 28 # include "prenv.h" 29 #endif 30 #include <prtypes.h> 31 32 #include "mozilla/IntegerPrintfMacros.h" 33 #include "mozilla/Printf.h" 34 #include "nsDebug.h" 35 36 #ifdef DEBUG 37 # define LOG_ERROR(str, args...) \ 38 PR_BEGIN_MACRO \ 39 mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \ 40 NS_WARNING(msg.get()); \ 41 PR_END_MACRO 42 #else 43 # define LOG_ERROR(str, args...) \ 44 do { /* nothing */ \ 45 } while (0) 46 #endif 47 48 namespace mozilla::ipc::shared_memory { 49 50 static inline void* toPointer(mach_vm_address_t aAddress) { 51 return reinterpret_cast<void*>(static_cast<uintptr_t>(aAddress)); 52 } 53 54 static inline mach_vm_address_t toVMAddress(void* aPointer) { 55 return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(aPointer)); 56 } 57 58 static Maybe<PlatformHandle> CreateImpl(size_t aSize, bool aFreezable) { 59 memory_object_size_t memoryObjectSize = round_page(aSize); 60 PlatformHandle handle; 61 62 kern_return_t kr = 63 mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, 0, 64 MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT, 65 getter_Transfers(handle), MACH_PORT_NULL); 66 if (kr != KERN_SUCCESS || memoryObjectSize < round_page(aSize)) { 67 LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n", aSize, 68 mach_error_string(kr), kr); 69 return Nothing(); 70 } 71 return Some(std::move(handle)); 72 } 73 74 bool Platform::Create(MutableHandle& aHandle, size_t aSize) { 75 if (auto ph = CreateImpl(aSize, false)) { 76 aHandle.mHandle = std::move(*ph); 77 aHandle.SetSize(aSize); 78 return true; 79 } 80 return false; 81 } 82 83 bool Platform::CreateFreezable(FreezableHandle& aHandle, size_t aSize) { 84 if (auto ph = CreateImpl(aSize, true)) { 85 aHandle.mHandle = std::move(*ph); 86 aHandle.SetSize(aSize); 87 return true; 88 } 89 return false; 90 } 91 92 PlatformHandle Platform::CloneHandle(const PlatformHandle& aHandle) { 93 return mozilla::RetainMachSendRight(aHandle.get()); 94 } 95 96 static Maybe<void*> MapMemory(uint64_t aOffset, size_t aSize, 97 void* aFixedAddress, 98 const mozilla::UniqueMachSendRight& aPort, 99 bool aReadOnly) { 100 kern_return_t kr; 101 mach_vm_address_t address = toVMAddress(aFixedAddress); 102 103 vm_prot_t vmProtection = VM_PROT_READ; 104 if (!aReadOnly) { 105 vmProtection |= VM_PROT_WRITE; 106 } 107 108 kr = mach_vm_map(mach_task_self(), &address, round_page(aSize), 0, 109 aFixedAddress ? VM_FLAGS_FIXED : VM_FLAGS_ANYWHERE, 110 aPort.get(), aOffset, false, vmProtection, vmProtection, 111 VM_INHERIT_NONE); 112 if (kr != KERN_SUCCESS) { 113 if (!aFixedAddress) { 114 LOG_ERROR( 115 "Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n", 116 aSize, mach_task_self(), mach_port_t(aPort.get()), 117 mach_error_string(kr), kr); 118 } 119 return Nothing(); 120 } 121 122 if (aFixedAddress && aFixedAddress != toPointer(address)) { 123 kr = vm_deallocate(mach_task_self(), address, aSize); 124 if (kr != KERN_SUCCESS) { 125 LOG_ERROR( 126 "Failed to unmap shared memory at unsuitable address " 127 "(%zu bytes) from %x, port %x. %s (%x)\n", 128 aSize, mach_task_self(), mach_port_t(aPort.get()), 129 mach_error_string(kr), kr); 130 } 131 return Nothing(); 132 } 133 134 return Some(toPointer(address)); 135 } 136 137 bool Platform::Freeze(FreezableHandle& aHandle) { 138 memory_object_size_t memoryObjectSize = round_page(aHandle.Size()); 139 140 mozilla::UniqueMachSendRight port; 141 142 // Temporarily map memory (as readonly) to get an address. 143 auto memory = MapMemory(0, memoryObjectSize, nullptr, aHandle.mHandle, true); 144 if (!memory) { 145 return false; 146 } 147 148 kern_return_t kr = mach_make_memory_entry_64( 149 mach_task_self(), &memoryObjectSize, 150 static_cast<memory_object_offset_t>(reinterpret_cast<uintptr_t>(*memory)), 151 VM_PROT_READ, getter_Transfers(port), MACH_PORT_NULL); 152 153 // Immediately try to deallocate, regardless of success. 154 { 155 kern_return_t kr = 156 vm_deallocate(mach_task_self(), toVMAddress(*memory), memoryObjectSize); 157 if (kr != KERN_SUCCESS) { 158 LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", 159 mach_error_string(kr), kr); 160 } 161 } 162 163 if (kr != KERN_SUCCESS || memoryObjectSize < round_page(aHandle.Size())) { 164 LOG_ERROR("Failed to make memory entry (%llu bytes). %s (%x)\n", 165 aHandle.Size(), mach_error_string(kr), kr); 166 return false; 167 } 168 169 aHandle.mHandle = std::move(port); 170 171 return true; 172 } 173 174 Maybe<void*> Platform::Map(const HandleBase& aHandle, uint64_t aOffset, 175 size_t aSize, void* aFixedAddress, bool aReadOnly) { 176 return MapMemory(aOffset, aSize, aFixedAddress, aHandle.mHandle, aReadOnly); 177 } 178 179 void Platform::Unmap(void* aMemory, size_t aSize) { 180 vm_address_t vm_address = toVMAddress(aMemory); 181 kern_return_t kr = 182 vm_deallocate(mach_task_self(), vm_address, round_page(aSize)); 183 if (kr != KERN_SUCCESS) { 184 LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", 185 mach_error_string(kr), kr); 186 } 187 } 188 189 bool Platform::Protect(char* aAddr, size_t aSize, Access aAccess) { 190 int flags = PROT_NONE; 191 if (aAccess & AccessRead) flags |= PROT_READ; 192 if (aAccess & AccessWrite) flags |= PROT_WRITE; 193 194 return 0 == mprotect(aAddr, aSize, flags); 195 } 196 197 void* Platform::FindFreeAddressSpace(size_t aSize) { 198 mach_vm_address_t address = 0; 199 aSize = round_page(aSize); 200 if (mach_vm_map(mach_task_self(), &address, aSize, 0, VM_FLAGS_ANYWHERE, 201 MEMORY_OBJECT_NULL, 0, false, VM_PROT_NONE, VM_PROT_NONE, 202 VM_INHERIT_NONE) != KERN_SUCCESS || 203 vm_deallocate(mach_task_self(), address, aSize) != KERN_SUCCESS) { 204 return nullptr; 205 } 206 return toPointer(address); 207 } 208 209 size_t Platform::PageSize() { 210 #if defined(XP_MACOSX) && defined(__x86_64__) 211 static std::atomic<size_t> sPageSizeOverride = 0; 212 213 if (sPageSizeOverride == 0) { 214 if (PR_GetEnv("MOZ_SHMEM_PAGESIZE_16K")) { 215 sPageSizeOverride = 16 * 1024; 216 } else { 217 sPageSizeOverride = sysconf(_SC_PAGESIZE); 218 } 219 } 220 return sPageSizeOverride; 221 #else 222 return sysconf(_SC_PAGESIZE); 223 #endif 224 } 225 226 size_t Platform::AllocationGranularity() { return PageSize(); } 227 228 bool Platform::IsSafeToMap(const PlatformHandle&) { return true; } 229 230 } // namespace mozilla::ipc::shared_memory