blob: 40acb942049bbc22eb541b1c696645d5801cee7f [file] [log] [blame]
Nicolas Capensc07dc4b2018-08-06 14:20:45 -04001// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Nicolas Capens1a3ce872018-10-10 10:42:36 -040015#include "ExecutableMemory.hpp"
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040016
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040017#include "Debug.hpp"
18
19#if defined(_WIN32)
20 #ifndef WIN32_LEAN_AND_MEAN
21 #define WIN32_LEAN_AND_MEAN
22 #endif
23 #include <windows.h>
24 #include <intrin.h>
Sergey Ulanov24e71922019-01-07 10:29:18 -080025#elif defined(__Fuchsia__)
26 #include <unistd.h>
27 #include <zircon/process.h>
28 #include <zircon/syscalls.h>
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040029#else
30 #include <errno.h>
31 #include <sys/mman.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34#endif
35
36#include <memory.h>
37
38#undef allocate
39#undef deallocate
40
41#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined (_M_X64)) && !defined(__x86__)
42#define __x86__
43#endif
44
Nicolas Capens157ba262019-12-10 17:49:14 -050045namespace rr {
46namespace {
47
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040048struct Allocation
49{
50// size_t bytes;
51 unsigned char *block;
52};
53
54void *allocateRaw(size_t bytes, size_t alignment)
55{
56 ASSERT((alignment & (alignment - 1)) == 0); // Power of 2 alignment.
57
58 #if defined(LINUX_ENABLE_NAMED_MMAP)
Nicolas Capensbecb44f2019-03-14 14:52:59 -040059 if(alignment < sizeof(void*))
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040060 {
Nicolas Capensbecb44f2019-03-14 14:52:59 -040061 return malloc(bytes);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040062 }
Nicolas Capensbecb44f2019-03-14 14:52:59 -040063 else
64 {
65 void *allocation;
66 int result = posix_memalign(&allocation, alignment, bytes);
67 if(result != 0)
68 {
69 errno = result;
70 allocation = nullptr;
71 }
72 return allocation;
73 }
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040074 #else
75 unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment];
76 unsigned char *aligned = nullptr;
77
78 if(block)
79 {
80 aligned = (unsigned char*)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment);
81 Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
82
83 // allocation->bytes = bytes;
84 allocation->block = block;
85 }
86
87 return aligned;
88 #endif
89}
90
91#if defined(LINUX_ENABLE_NAMED_MMAP)
92// Create a file descriptor for anonymous memory with the given
93// name. Returns -1 on failure.
94// TODO: remove once libc wrapper exists.
95int memfd_create(const char* name, unsigned int flags)
96{
97 #if __aarch64__
98 #define __NR_memfd_create 279
99 #elif __arm__
100 #define __NR_memfd_create 279
101 #elif __powerpc64__
102 #define __NR_memfd_create 360
103 #elif __i386__
104 #define __NR_memfd_create 356
105 #elif __x86_64__
106 #define __NR_memfd_create 319
107 #endif /* __NR_memfd_create__ */
108 #ifdef __NR_memfd_create
109 // In the event of no system call this returns -1 with errno set
110 // as ENOSYS.
111 return syscall(__NR_memfd_create, name, flags);
112 #else
113 return -1;
114 #endif
115}
116
117// Returns a file descriptor for use with an anonymous mmap, if
118// memfd_create fails, -1 is returned. Note, the mappings should be
119// MAP_PRIVATE so that underlying pages aren't shared.
120int anonymousFd()
121{
122 static int fd = memfd_create("SwiftShader JIT", 0);
123 return fd;
124}
125
126// Ensure there is enough space in the "anonymous" fd for length.
127void ensureAnonFileSize(int anonFd, size_t length)
128{
129 static size_t fileSize = 0;
130 if(length > fileSize)
131 {
132 ftruncate(anonFd, length);
133 fileSize = length;
134 }
135}
136#endif // defined(LINUX_ENABLE_NAMED_MMAP)
137
138} // anonymous namespace
139
140size_t memoryPageSize()
141{
142 static int pageSize = 0;
143
144 if(pageSize == 0)
145 {
146 #if defined(_WIN32)
147 SYSTEM_INFO systemInfo;
148 GetSystemInfo(&systemInfo);
149 pageSize = systemInfo.dwPageSize;
150 #else
151 pageSize = sysconf(_SC_PAGESIZE);
152 #endif
153 }
154
155 return pageSize;
156}
157
158void *allocate(size_t bytes, size_t alignment)
159{
160 void *memory = allocateRaw(bytes, alignment);
161
162 if(memory)
163 {
164 memset(memory, 0, bytes);
165 }
166
167 return memory;
168}
169
170void deallocate(void *memory)
171{
172 #if defined(LINUX_ENABLE_NAMED_MMAP)
173 free(memory);
174 #else
175 if(memory)
176 {
177 unsigned char *aligned = (unsigned char*)memory;
178 Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
179
180 delete[] allocation->block;
181 }
182 #endif
183}
184
Sergey Ulanov24e71922019-01-07 10:29:18 -0800185// Rounds |x| up to a multiple of |m|, where |m| is a power of 2.
186inline uintptr_t roundUp(uintptr_t x, uintptr_t m)
187{
188 ASSERT(m > 0 && (m & (m - 1)) == 0); // |m| must be a power of 2.
189 return (x + m - 1) & ~(m - 1);
190}
191
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400192void *allocateExecutable(size_t bytes)
193{
194 size_t pageSize = memoryPageSize();
Sergey Ulanov24e71922019-01-07 10:29:18 -0800195 size_t length = roundUp(bytes, pageSize);
Nicolas Capens0a94b952019-07-29 17:22:34 -0400196 void *mapping = nullptr;
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400197
198 #if defined(LINUX_ENABLE_NAMED_MMAP)
199 // Try to name the memory region for the executable code,
200 // to aid profilers.
201 int anonFd = anonymousFd();
202 if(anonFd == -1)
203 {
204 mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
205 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
206 }
207 else
208 {
209 ensureAnonFileSize(anonFd, length);
210 mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
211 MAP_PRIVATE, anonFd, 0);
212 }
213
214 if(mapping == MAP_FAILED)
215 {
216 mapping = nullptr;
217 }
Sergey Ulanov24e71922019-01-07 10:29:18 -0800218 #elif defined(__Fuchsia__)
219 zx_handle_t vmo;
David Stevensb8260a82019-06-11 16:28:50 -0700220 if (zx_vmo_create(length, 0, &vmo) != ZX_OK) {
Sergey Ulanov24e71922019-01-07 10:29:18 -0800221 return nullptr;
222 }
Matthew Dempsky77630f12019-01-29 17:51:59 -0800223 if (zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo) != ZX_OK) {
224 return nullptr;
225 }
Sergey Ulanov24e71922019-01-07 10:29:18 -0800226 zx_vaddr_t reservation;
227 zx_status_t status = zx_vmar_map(
Wez314ad812019-01-25 20:52:45 -0800228 zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
Sergey Ulanov24e71922019-01-07 10:29:18 -0800229 0, vmo, 0, length, &reservation);
230 zx_handle_close(vmo);
231 if (status != ZX_OK) {
232 return nullptr;
233 }
234
Sergey Ulanov0eb5c832019-07-26 10:35:10 -0700235 // zx_vmar_map() returns page-aligned address.
236 ASSERT(roundUp(reservation, pageSize) == reservation);
Sergey Ulanov24e71922019-01-07 10:29:18 -0800237
Sergey Ulanov0eb5c832019-07-26 10:35:10 -0700238 mapping = reinterpret_cast<void*>(reservation);
Nicolas Capens0a94b952019-07-29 17:22:34 -0400239 #elif defined(__APPLE__)
240 // On macOS 10.14 and higher, executables that are code signed with the
241 // "runtime" option cannot execute writable memory by default. They can opt
242 // into this capability by specifying the "com.apple.security.cs.allow-jit"
243 // code signing entitlement and allocating the region with the MAP_JIT flag.
244 mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
245 MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT, -1, 0);
246
247 if(mapping == MAP_FAILED)
248 {
Nicolas Capensc39e7c72019-07-30 16:42:22 -0400249 // Retry without MAP_JIT (for older macOS versions).
250 mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
251 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
252 }
253
254 if(mapping == MAP_FAILED)
255 {
Nicolas Capens0a94b952019-07-29 17:22:34 -0400256 mapping = nullptr;
257 }
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400258 #else
259 mapping = allocate(length, pageSize);
260 #endif
261
262 return mapping;
263}
264
265void markExecutable(void *memory, size_t bytes)
266{
267 #if defined(_WIN32)
268 unsigned long oldProtection;
269 VirtualProtect(memory, bytes, PAGE_EXECUTE_READ, &oldProtection);
Sergey Ulanov24e71922019-01-07 10:29:18 -0800270 #elif defined(__Fuchsia__)
Sergey Ulanov0eb5c832019-07-26 10:35:10 -0700271 size_t pageSize = memoryPageSize();
272 size_t length = roundUp(bytes, pageSize);
Sergey Ulanov24e71922019-01-07 10:29:18 -0800273 zx_status_t status = zx_vmar_protect(
Wez314ad812019-01-25 20:52:45 -0800274 zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE,
Sergey Ulanov0eb5c832019-07-26 10:35:10 -0700275 reinterpret_cast<zx_vaddr_t>(memory), length);
276 ASSERT(status == ZX_OK);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400277 #else
278 mprotect(memory, bytes, PROT_READ | PROT_EXEC);
279 #endif
280}
281
282void deallocateExecutable(void *memory, size_t bytes)
283{
284 #if defined(_WIN32)
285 unsigned long oldProtection;
286 VirtualProtect(memory, bytes, PAGE_READWRITE, &oldProtection);
287 deallocate(memory);
Nicolas Capens0a94b952019-07-29 17:22:34 -0400288 #elif defined(LINUX_ENABLE_NAMED_MMAP) || defined(__APPLE__)
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400289 size_t pageSize = memoryPageSize();
290 size_t length = (bytes + pageSize - 1) & ~(pageSize - 1);
291 munmap(memory, length);
Sergey Ulanov24e71922019-01-07 10:29:18 -0800292 #elif defined(__Fuchsia__)
Sergey Ulanov0eb5c832019-07-26 10:35:10 -0700293 size_t pageSize = memoryPageSize();
294 size_t length = roundUp(bytes, pageSize);
295 zx_status_t status = zx_vmar_unmap(
296 zx_vmar_root_self(), reinterpret_cast<zx_vaddr_t>(memory), length);
297 ASSERT(status == ZX_OK);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400298 #else
299 mprotect(memory, bytes, PROT_READ | PROT_WRITE);
300 deallocate(memory);
301 #endif
302}
Nicolas Capens157ba262019-12-10 17:49:14 -0500303
304} // namespace rr