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