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