1 // Copyright 2012 the V8 project 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 // Platform-specific code for MacOS goes here. Code shared between iOS and
6 // macOS is in platform-darwin.cc, while the POSIX-compatible are in in
7 // platform-posix.cc.
8
9 #include <mach/mach.h>
10 #include <mach/mach_vm.h>
11 #include <mach/vm_map.h>
12
13 #include "src/base/platform/platform.h"
14
15 namespace v8 {
16 namespace base {
17
18 namespace {
19
GetVMProtFromMemoryPermission(OS::MemoryPermission access)20 vm_prot_t GetVMProtFromMemoryPermission(OS::MemoryPermission access) {
21 switch (access) {
22 case OS::MemoryPermission::kNoAccess:
23 case OS::MemoryPermission::kNoAccessWillJitLater:
24 return VM_PROT_NONE;
25 case OS::MemoryPermission::kRead:
26 return VM_PROT_READ;
27 case OS::MemoryPermission::kReadWrite:
28 return VM_PROT_READ | VM_PROT_WRITE;
29 case OS::MemoryPermission::kReadWriteExecute:
30 return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
31 case OS::MemoryPermission::kReadExecute:
32 return VM_PROT_READ | VM_PROT_EXECUTE;
33 }
34 UNREACHABLE();
35 }
36
mach_vm_map_wrapper(mach_vm_address_t * address,mach_vm_size_t size,int flags,mach_port_t port,memory_object_offset_t offset,vm_prot_t prot)37 kern_return_t mach_vm_map_wrapper(mach_vm_address_t* address,
38 mach_vm_size_t size, int flags,
39 mach_port_t port,
40 memory_object_offset_t offset,
41 vm_prot_t prot) {
42 vm_prot_t current_prot = prot;
43 vm_prot_t maximum_prot = current_prot;
44 return mach_vm_map(mach_task_self(), address, size, 0, flags, port, offset,
45 FALSE, current_prot, maximum_prot, VM_INHERIT_NONE);
46 }
47
48 } // namespace
49
50 // static
CreateSharedMemoryHandleForTesting(size_t size)51 PlatformSharedMemoryHandle OS::CreateSharedMemoryHandleForTesting(size_t size) {
52 mach_vm_size_t vm_size = size;
53 mach_port_t port;
54 kern_return_t kr = mach_make_memory_entry_64(
55 mach_task_self(), &vm_size, 0,
56 MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, &port,
57 MACH_PORT_NULL);
58 if (kr != KERN_SUCCESS) return kInvalidSharedMemoryHandle;
59 return SharedMemoryHandleFromMachMemoryEntry(port);
60 }
61
62 // static
DestroySharedMemoryHandle(PlatformSharedMemoryHandle handle)63 void OS::DestroySharedMemoryHandle(PlatformSharedMemoryHandle handle) {
64 DCHECK_NE(kInvalidSharedMemoryHandle, handle);
65 mach_port_t port = MachMemoryEntryFromSharedMemoryHandle(handle);
66 CHECK_EQ(KERN_SUCCESS, mach_port_deallocate(mach_task_self(), port));
67 }
68
69 // static
AllocateShared(void * hint,size_t size,MemoryPermission access,PlatformSharedMemoryHandle handle,uint64_t offset)70 void* OS::AllocateShared(void* hint, size_t size, MemoryPermission access,
71 PlatformSharedMemoryHandle handle, uint64_t offset) {
72 DCHECK_EQ(0, size % AllocatePageSize());
73
74 mach_vm_address_t addr = reinterpret_cast<mach_vm_address_t>(hint);
75 vm_prot_t prot = GetVMProtFromMemoryPermission(access);
76 mach_port_t shared_mem_port = MachMemoryEntryFromSharedMemoryHandle(handle);
77 kern_return_t kr = mach_vm_map_wrapper(&addr, size, VM_FLAGS_FIXED,
78 shared_mem_port, offset, prot);
79
80 if (kr != KERN_SUCCESS) {
81 // Retry without hint.
82 kr = mach_vm_map_wrapper(&addr, size, VM_FLAGS_ANYWHERE, shared_mem_port,
83 offset, prot);
84 }
85
86 if (kr != KERN_SUCCESS) return nullptr;
87 return reinterpret_cast<void*>(addr);
88 }
89
90 // static
RemapPages(const void * address,size_t size,void * new_address,MemoryPermission access)91 bool OS::RemapPages(const void* address, size_t size, void* new_address,
92 MemoryPermission access) {
93 DCHECK(IsAligned(reinterpret_cast<uintptr_t>(address), AllocatePageSize()));
94 DCHECK(
95 IsAligned(reinterpret_cast<uintptr_t>(new_address), AllocatePageSize()));
96 DCHECK(IsAligned(size, AllocatePageSize()));
97
98 vm_prot_t cur_protection = GetVMProtFromMemoryPermission(access);
99 vm_prot_t max_protection;
100 // Asks the kernel to remap *on top* of an existing mapping, rather than
101 // copying the data.
102 int flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE;
103 mach_vm_address_t target = reinterpret_cast<mach_vm_address_t>(new_address);
104 kern_return_t ret =
105 mach_vm_remap(mach_task_self(), &target, size, 0, flags, mach_task_self(),
106 reinterpret_cast<mach_vm_address_t>(address), FALSE,
107 &cur_protection, &max_protection, VM_INHERIT_NONE);
108
109 if (ret != KERN_SUCCESS) return false;
110
111 // Did we get the address we wanted?
112 CHECK_EQ(new_address, reinterpret_cast<void*>(target));
113
114 return true;
115 }
116
AllocateShared(void * address,size_t size,OS::MemoryPermission access,PlatformSharedMemoryHandle handle,uint64_t offset)117 bool AddressSpaceReservation::AllocateShared(void* address, size_t size,
118 OS::MemoryPermission access,
119 PlatformSharedMemoryHandle handle,
120 uint64_t offset) {
121 DCHECK(Contains(address, size));
122
123 vm_prot_t prot = GetVMProtFromMemoryPermission(access);
124 mach_vm_address_t addr = reinterpret_cast<mach_vm_address_t>(address);
125 mach_port_t shared_mem_port = MachMemoryEntryFromSharedMemoryHandle(handle);
126 kern_return_t kr =
127 mach_vm_map_wrapper(&addr, size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
128 shared_mem_port, offset, prot);
129 return kr == KERN_SUCCESS;
130 }
131
132 } // namespace base
133 } // namespace v8
134