summaryrefslogtreecommitdiff
path: root/deps/v8/src/base/platform/platform-macos.cc
blob: a97295b4ccf52ede902df89128e05f6d3ebc5bd6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Platform-specific code for MacOS goes here. Code shared between iOS and
// macOS is in platform-darwin.cc, while the POSIX-compatible are in in
// platform-posix.cc.

#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <mach/vm_map.h>

#include "src/base/platform/platform.h"

namespace v8 {
namespace base {

namespace {

vm_prot_t GetVMProtFromMemoryPermission(OS::MemoryPermission access) {
  switch (access) {
    case OS::MemoryPermission::kNoAccess:
    case OS::MemoryPermission::kNoAccessWillJitLater:
      return VM_PROT_NONE;
    case OS::MemoryPermission::kRead:
      return VM_PROT_READ;
    case OS::MemoryPermission::kReadWrite:
      return VM_PROT_READ | VM_PROT_WRITE;
    case OS::MemoryPermission::kReadWriteExecute:
      return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
    case OS::MemoryPermission::kReadExecute:
      return VM_PROT_READ | VM_PROT_EXECUTE;
  }
  UNREACHABLE();
}

kern_return_t 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) {
  vm_prot_t current_prot = prot;
  vm_prot_t maximum_prot = current_prot;
  return mach_vm_map(mach_task_self(), address, size, 0, flags, port, offset,
                     FALSE, current_prot, maximum_prot, VM_INHERIT_NONE);
}

}  // namespace

// static
PlatformSharedMemoryHandle OS::CreateSharedMemoryHandleForTesting(size_t size) {
  mach_vm_size_t vm_size = size;
  mach_port_t port;
  kern_return_t kr = mach_make_memory_entry_64(
      mach_task_self(), &vm_size, 0,
      MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, &port,
      MACH_PORT_NULL);
  if (kr != KERN_SUCCESS) return kInvalidSharedMemoryHandle;
  return SharedMemoryHandleFromMachMemoryEntry(port);
}

// static
void OS::DestroySharedMemoryHandle(PlatformSharedMemoryHandle handle) {
  DCHECK_NE(kInvalidSharedMemoryHandle, handle);
  mach_port_t port = MachMemoryEntryFromSharedMemoryHandle(handle);
  CHECK_EQ(KERN_SUCCESS, mach_port_deallocate(mach_task_self(), port));
}

// static
void* OS::AllocateShared(void* hint, size_t size, MemoryPermission access,
                         PlatformSharedMemoryHandle handle, uint64_t offset) {
  DCHECK_EQ(0, size % AllocatePageSize());

  mach_vm_address_t addr = reinterpret_cast<mach_vm_address_t>(hint);
  vm_prot_t prot = GetVMProtFromMemoryPermission(access);
  mach_port_t shared_mem_port = MachMemoryEntryFromSharedMemoryHandle(handle);
  kern_return_t kr = mach_vm_map_wrapper(&addr, size, VM_FLAGS_FIXED,
                                         shared_mem_port, offset, prot);

  if (kr != KERN_SUCCESS) {
    // Retry without hint.
    kr = mach_vm_map_wrapper(&addr, size, VM_FLAGS_ANYWHERE, shared_mem_port,
                             offset, prot);
  }

  if (kr != KERN_SUCCESS) return nullptr;
  return reinterpret_cast<void*>(addr);
}

// static
bool OS::RemapPages(const void* address, size_t size, void* new_address,
                    MemoryPermission access) {
  DCHECK(IsAligned(reinterpret_cast<uintptr_t>(address), AllocatePageSize()));
  DCHECK(
      IsAligned(reinterpret_cast<uintptr_t>(new_address), AllocatePageSize()));
  DCHECK(IsAligned(size, AllocatePageSize()));

  vm_prot_t cur_protection = GetVMProtFromMemoryPermission(access);
  vm_prot_t max_protection;
  // Asks the kernel to remap *on top* of an existing mapping, rather than
  // copying the data.
  int flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE;
  mach_vm_address_t target = reinterpret_cast<mach_vm_address_t>(new_address);
  kern_return_t ret =
      mach_vm_remap(mach_task_self(), &target, size, 0, flags, mach_task_self(),
                    reinterpret_cast<mach_vm_address_t>(address), FALSE,
                    &cur_protection, &max_protection, VM_INHERIT_NONE);

  if (ret != KERN_SUCCESS) return false;

  // Did we get the address we wanted?
  CHECK_EQ(new_address, reinterpret_cast<void*>(target));

  return true;
}

bool AddressSpaceReservation::AllocateShared(void* address, size_t size,
                                             OS::MemoryPermission access,
                                             PlatformSharedMemoryHandle handle,
                                             uint64_t offset) {
  DCHECK(Contains(address, size));

  vm_prot_t prot = GetVMProtFromMemoryPermission(access);
  mach_vm_address_t addr = reinterpret_cast<mach_vm_address_t>(address);
  mach_port_t shared_mem_port = MachMemoryEntryFromSharedMemoryHandle(handle);
  kern_return_t kr =
      mach_vm_map_wrapper(&addr, size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
                          shared_mem_port, offset, prot);
  return kr == KERN_SUCCESS;
}

}  // namespace base
}  // namespace v8