blob: bbde2ab64ce42563e7f7eeb7d1153c93a6c107d6 [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
45namespace rr
46{
47namespace
48{
49struct Allocation
50{
51// size_t bytes;
52 unsigned char *block;
53};
54
55void *allocateRaw(size_t bytes, size_t alignment)
56{
57 ASSERT((alignment & (alignment - 1)) == 0); // Power of 2 alignment.
58
59 #if defined(LINUX_ENABLE_NAMED_MMAP)
Nicolas Capensbecb44f2019-03-14 14:52:59 -040060 if(alignment < sizeof(void*))
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040061 {
Nicolas Capensbecb44f2019-03-14 14:52:59 -040062 return malloc(bytes);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040063 }
Nicolas Capensbecb44f2019-03-14 14:52:59 -040064 else
65 {
66 void *allocation;
67 int result = posix_memalign(&allocation, alignment, bytes);
68 if(result != 0)
69 {
70 errno = result;
71 allocation = nullptr;
72 }
73 return allocation;
74 }
Nicolas Capensc07dc4b2018-08-06 14:20:45 -040075 #else
76 unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment];
77 unsigned char *aligned = nullptr;
78
79 if(block)
80 {
81 aligned = (unsigned char*)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment);
82 Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
83
84 // allocation->bytes = bytes;
85 allocation->block = block;
86 }
87
88 return aligned;
89 #endif
90}
91
92#if defined(LINUX_ENABLE_NAMED_MMAP)
93// Create a file descriptor for anonymous memory with the given
94// name. Returns -1 on failure.
95// TODO: remove once libc wrapper exists.
96int memfd_create(const char* name, unsigned int flags)
97{
98 #if __aarch64__
99 #define __NR_memfd_create 279
100 #elif __arm__
101 #define __NR_memfd_create 279
102 #elif __powerpc64__
103 #define __NR_memfd_create 360
104 #elif __i386__
105 #define __NR_memfd_create 356
106 #elif __x86_64__
107 #define __NR_memfd_create 319
108 #endif /* __NR_memfd_create__ */
109 #ifdef __NR_memfd_create
110 // In the event of no system call this returns -1 with errno set
111 // as ENOSYS.
112 return syscall(__NR_memfd_create, name, flags);
113 #else
114 return -1;
115 #endif
116}
117
118// Returns a file descriptor for use with an anonymous mmap, if
119// memfd_create fails, -1 is returned. Note, the mappings should be
120// MAP_PRIVATE so that underlying pages aren't shared.
121int anonymousFd()
122{
123 static int fd = memfd_create("SwiftShader JIT", 0);
124 return fd;
125}
126
127// Ensure there is enough space in the "anonymous" fd for length.
128void ensureAnonFileSize(int anonFd, size_t length)
129{
130 static size_t fileSize = 0;
131 if(length > fileSize)
132 {
133 ftruncate(anonFd, length);
134 fileSize = length;
135 }
136}
137#endif // defined(LINUX_ENABLE_NAMED_MMAP)
138
139} // anonymous namespace
140
141size_t memoryPageSize()
142{
143 static int pageSize = 0;
144
145 if(pageSize == 0)
146 {
147 #if defined(_WIN32)
148 SYSTEM_INFO systemInfo;
149 GetSystemInfo(&systemInfo);
150 pageSize = systemInfo.dwPageSize;
151 #else
152 pageSize = sysconf(_SC_PAGESIZE);
153 #endif
154 }
155
156 return pageSize;
157}
158
159void *allocate(size_t bytes, size_t alignment)
160{
161 void *memory = allocateRaw(bytes, alignment);
162
163 if(memory)
164 {
165 memset(memory, 0, bytes);
166 }
167
168 return memory;
169}
170
171void deallocate(void *memory)
172{
173 #if defined(LINUX_ENABLE_NAMED_MMAP)
174 free(memory);
175 #else
176 if(memory)
177 {
178 unsigned char *aligned = (unsigned char*)memory;
179 Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
180
181 delete[] allocation->block;
182 }
183 #endif
184}
185
Sergey Ulanov24e71922019-01-07 10:29:18 -0800186// Rounds |x| up to a multiple of |m|, where |m| is a power of 2.
187inline uintptr_t roundUp(uintptr_t x, uintptr_t m)
188{
189 ASSERT(m > 0 && (m & (m - 1)) == 0); // |m| must be a power of 2.
190 return (x + m - 1) & ~(m - 1);
191}
192
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400193void *allocateExecutable(size_t bytes)
194{
195 size_t pageSize = memoryPageSize();
Sergey Ulanov24e71922019-01-07 10:29:18 -0800196 size_t length = roundUp(bytes, pageSize);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400197 void *mapping;
198
199 #if defined(LINUX_ENABLE_NAMED_MMAP)
200 // Try to name the memory region for the executable code,
201 // to aid profilers.
202 int anonFd = anonymousFd();
203 if(anonFd == -1)
204 {
205 mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
206 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
207 }
208 else
209 {
210 ensureAnonFileSize(anonFd, length);
211 mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
212 MAP_PRIVATE, anonFd, 0);
213 }
214
215 if(mapping == MAP_FAILED)
216 {
217 mapping = nullptr;
218 }
Sergey Ulanov24e71922019-01-07 10:29:18 -0800219 #elif defined(__Fuchsia__)
220 zx_handle_t vmo;
221 if (zx_vmo_create(length, ZX_VMO_NON_RESIZABLE, &vmo) != ZX_OK) {
222 return nullptr;
223 }
Matthew Dempsky77630f12019-01-29 17:51:59 -0800224 if (zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo) != ZX_OK) {
225 return nullptr;
226 }
Sergey Ulanov24e71922019-01-07 10:29:18 -0800227 zx_vaddr_t reservation;
228 zx_status_t status = zx_vmar_map(
Wez314ad812019-01-25 20:52:45 -0800229 zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
Sergey Ulanov24e71922019-01-07 10:29:18 -0800230 0, vmo, 0, length, &reservation);
231 zx_handle_close(vmo);
232 if (status != ZX_OK) {
233 return nullptr;
234 }
235
236 zx_vaddr_t alignedReservation = roundUp(reservation, pageSize);
237 mapping = reinterpret_cast<void*>(alignedReservation);
238
239 // Unmap extra memory reserved before the block.
240 if (alignedReservation != reservation) {
241 size_t prefix_size = alignedReservation - reservation;
242 status =
243 zx_vmar_unmap(zx_vmar_root_self(), reservation, prefix_size);
244 ASSERT(status == ZX_OK);
245 length -= prefix_size;
246 }
247
248 // Unmap extra memory at the end.
249 if (length > bytes) {
250 status = zx_vmar_unmap(
251 zx_vmar_root_self(), alignedReservation + bytes,
252 length - bytes);
253 ASSERT(status == ZX_OK);
254 }
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400255 #else
256 mapping = allocate(length, pageSize);
257 #endif
258
259 return mapping;
260}
261
262void markExecutable(void *memory, size_t bytes)
263{
264 #if defined(_WIN32)
265 unsigned long oldProtection;
266 VirtualProtect(memory, bytes, PAGE_EXECUTE_READ, &oldProtection);
Sergey Ulanov24e71922019-01-07 10:29:18 -0800267 #elif defined(__Fuchsia__)
268 zx_status_t status = zx_vmar_protect(
Wez314ad812019-01-25 20:52:45 -0800269 zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE,
Sergey Ulanov24e71922019-01-07 10:29:18 -0800270 reinterpret_cast<zx_vaddr_t>(memory), bytes);
271 ASSERT(status != ZX_OK);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400272 #else
273 mprotect(memory, bytes, PROT_READ | PROT_EXEC);
274 #endif
275}
276
277void deallocateExecutable(void *memory, size_t bytes)
278{
279 #if defined(_WIN32)
280 unsigned long oldProtection;
281 VirtualProtect(memory, bytes, PAGE_READWRITE, &oldProtection);
282 deallocate(memory);
283 #elif defined(LINUX_ENABLE_NAMED_MMAP)
284 size_t pageSize = memoryPageSize();
285 size_t length = (bytes + pageSize - 1) & ~(pageSize - 1);
286 munmap(memory, length);
Sergey Ulanov24e71922019-01-07 10:29:18 -0800287 #elif defined(__Fuchsia__)
288 zx_vmar_unmap(zx_vmar_root_self(), reinterpret_cast<zx_vaddr_t>(memory),
289 bytes);
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400290 #else
291 mprotect(memory, bytes, PROT_READ | PROT_WRITE);
292 deallocate(memory);
293 #endif
294}
Nicolas Capensc07dc4b2018-08-06 14:20:45 -0400295}