1 // Copyright 2017 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 #include "src/base/page-allocator.h"
6
7 #include "src/base/platform/platform.h"
8
9 #if V8_OS_MACOSX
10 #include <sys/mman.h> // For MAP_JIT.
11 #endif
12
13 namespace v8 {
14 namespace base {
15
16 #define STATIC_ASSERT_ENUM(a, b) \
17 static_assert(static_cast<int>(a) == static_cast<int>(b), \
18 "mismatching enum: " #a)
19
20 STATIC_ASSERT_ENUM(PageAllocator::kNoAccess,
21 base::OS::MemoryPermission::kNoAccess);
22 STATIC_ASSERT_ENUM(PageAllocator::kReadWrite,
23 base::OS::MemoryPermission::kReadWrite);
24 STATIC_ASSERT_ENUM(PageAllocator::kReadWriteExecute,
25 base::OS::MemoryPermission::kReadWriteExecute);
26 STATIC_ASSERT_ENUM(PageAllocator::kReadExecute,
27 base::OS::MemoryPermission::kReadExecute);
28 STATIC_ASSERT_ENUM(PageAllocator::kNoAccessWillJitLater,
29 base::OS::MemoryPermission::kNoAccessWillJitLater);
30
31 #undef STATIC_ASSERT_ENUM
32
PageAllocator()33 PageAllocator::PageAllocator()
34 : allocate_page_size_(base::OS::AllocatePageSize()),
35 commit_page_size_(base::OS::CommitPageSize()) {}
36
SetRandomMmapSeed(int64_t seed)37 void PageAllocator::SetRandomMmapSeed(int64_t seed) {
38 base::OS::SetRandomMmapSeed(seed);
39 }
40
GetRandomMmapAddr()41 void* PageAllocator::GetRandomMmapAddr() {
42 return base::OS::GetRandomMmapAddr();
43 }
44
AllocatePages(void * hint,size_t size,size_t alignment,PageAllocator::Permission access)45 void* PageAllocator::AllocatePages(void* hint, size_t size, size_t alignment,
46 PageAllocator::Permission access) {
47 #if !(V8_OS_MACOSX && V8_HOST_ARCH_ARM64 && defined(MAP_JIT))
48 // kNoAccessWillJitLater is only used on Apple Silicon. Map it to regular
49 // kNoAccess on other platforms, so code doesn't have to handle both enum
50 // values.
51 if (access == PageAllocator::kNoAccessWillJitLater) {
52 access = PageAllocator::kNoAccess;
53 }
54 #endif
55 return base::OS::Allocate(hint, size, alignment,
56 static_cast<base::OS::MemoryPermission>(access));
57 }
58
59 class SharedMemoryMapping : public ::v8::PageAllocator::SharedMemoryMapping {
60 public:
SharedMemoryMapping(PageAllocator * page_allocator,void * ptr,size_t size)61 explicit SharedMemoryMapping(PageAllocator* page_allocator, void* ptr,
62 size_t size)
63 : page_allocator_(page_allocator), ptr_(ptr), size_(size) {}
~SharedMemoryMapping()64 ~SharedMemoryMapping() override { page_allocator_->FreePages(ptr_, size_); }
GetMemory() const65 void* GetMemory() const override { return ptr_; }
66
67 private:
68 PageAllocator* page_allocator_;
69 void* ptr_;
70 size_t size_;
71 };
72
73 class SharedMemory : public ::v8::PageAllocator::SharedMemory {
74 public:
SharedMemory(PageAllocator * allocator,void * memory,size_t size)75 SharedMemory(PageAllocator* allocator, void* memory, size_t size)
76 : allocator_(allocator), ptr_(memory), size_(size) {}
GetMemory() const77 void* GetMemory() const override { return ptr_; }
GetSize() const78 size_t GetSize() const override { return size_; }
RemapTo(void * new_address) const79 std::unique_ptr<::v8::PageAllocator::SharedMemoryMapping> RemapTo(
80 void* new_address) const override {
81 if (allocator_->RemapShared(ptr_, new_address, size_)) {
82 return std::make_unique<SharedMemoryMapping>(allocator_, new_address,
83 size_);
84 } else {
85 return {};
86 }
87 }
88
~SharedMemory()89 ~SharedMemory() override { allocator_->FreePages(ptr_, size_); }
90
91 private:
92 PageAllocator* allocator_;
93 void* ptr_;
94 size_t size_;
95 };
96
CanAllocateSharedPages()97 bool PageAllocator::CanAllocateSharedPages() {
98 #ifdef V8_OS_LINUX
99 return true;
100 #else
101 return false;
102 #endif
103 }
104
105 std::unique_ptr<v8::PageAllocator::SharedMemory>
AllocateSharedPages(size_t size,const void * original_address)106 PageAllocator::AllocateSharedPages(size_t size, const void* original_address) {
107 #ifdef V8_OS_LINUX
108 void* ptr =
109 base::OS::AllocateShared(size, base::OS::MemoryPermission::kReadWrite);
110 CHECK_NOT_NULL(ptr);
111 memcpy(ptr, original_address, size);
112 bool success = base::OS::SetPermissions(
113 ptr, size, base::OS::MemoryPermission::kReadWrite);
114 CHECK(success);
115
116 auto shared_memory =
117 std::make_unique<v8::base::SharedMemory>(this, ptr, size);
118 return shared_memory;
119 #else
120 return {};
121 #endif
122 }
123
RemapShared(void * old_address,void * new_address,size_t size)124 void* PageAllocator::RemapShared(void* old_address, void* new_address,
125 size_t size) {
126 #ifdef V8_OS_LINUX
127 return base::OS::RemapShared(old_address, new_address, size);
128 #else
129 return nullptr;
130 #endif
131 }
132
FreePages(void * address,size_t size)133 bool PageAllocator::FreePages(void* address, size_t size) {
134 return base::OS::Free(address, size);
135 }
136
ReleasePages(void * address,size_t size,size_t new_size)137 bool PageAllocator::ReleasePages(void* address, size_t size, size_t new_size) {
138 DCHECK_LT(new_size, size);
139 return base::OS::Release(reinterpret_cast<uint8_t*>(address) + new_size,
140 size - new_size);
141 }
142
SetPermissions(void * address,size_t size,PageAllocator::Permission access)143 bool PageAllocator::SetPermissions(void* address, size_t size,
144 PageAllocator::Permission access) {
145 return base::OS::SetPermissions(
146 address, size, static_cast<base::OS::MemoryPermission>(access));
147 }
148
DiscardSystemPages(void * address,size_t size)149 bool PageAllocator::DiscardSystemPages(void* address, size_t size) {
150 return base::OS::DiscardSystemPages(address, size);
151 }
152
153 } // namespace base
154 } // namespace v8
155