CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/MemoryUtil.cpp
Views: 1401
1
// Copyright (C) 2003 Dolphin Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official SVN repository and contact information can be found at
16
// http://code.google.com/p/dolphin-emu/
17
18
#include "ppsspp_config.h"
19
20
#if !PPSSPP_PLATFORM(SWITCH)
21
#include <cstring>
22
#include <cstdlib>
23
24
#include "Common/CommonTypes.h"
25
#include "Common/Log.h"
26
#include "Common/MemoryUtil.h"
27
#include "Common/StringUtils.h"
28
#include "Common/SysError.h"
29
#include "Common/Data/Text/Parsers.h"
30
31
#ifdef _WIN32
32
#include "Common/CommonWindows.h"
33
#else
34
#include <errno.h>
35
#include <stdio.h>
36
#endif
37
38
#ifdef __APPLE__
39
#include <sys/types.h>
40
#include <sys/mman.h>
41
#include <mach/vm_param.h>
42
#endif
43
44
#ifndef _WIN32
45
#include <unistd.h>
46
#endif
47
static int hint_location;
48
#ifdef __APPLE__
49
#define MEM_PAGE_SIZE (PAGE_SIZE)
50
#elif defined(_WIN32)
51
static SYSTEM_INFO sys_info;
52
#define MEM_PAGE_SIZE (uintptr_t)(sys_info.dwPageSize)
53
#else
54
#define MEM_PAGE_SIZE (getpagesize())
55
#endif
56
57
#define MEM_PAGE_MASK ((MEM_PAGE_SIZE)-1)
58
#define ppsspp_round_page(x) ((((uintptr_t)(x)) + MEM_PAGE_MASK) & ~(MEM_PAGE_MASK))
59
60
#ifdef _WIN32
61
// Win32 memory protection flags are odd...
62
static uint32_t ConvertProtFlagsWin32(uint32_t flags) {
63
uint32_t protect = 0;
64
switch (flags) {
65
case 0: protect = PAGE_NOACCESS; break;
66
case MEM_PROT_READ: protect = PAGE_READONLY; break;
67
case MEM_PROT_WRITE: protect = PAGE_READWRITE; break; // Can't set write-only
68
case MEM_PROT_EXEC: protect = PAGE_EXECUTE; break;
69
case MEM_PROT_READ | MEM_PROT_EXEC: protect = PAGE_EXECUTE_READ; break;
70
case MEM_PROT_WRITE | MEM_PROT_EXEC: protect = PAGE_EXECUTE_READWRITE; break; // Can't set write-only
71
case MEM_PROT_READ | MEM_PROT_WRITE: protect = PAGE_READWRITE; break;
72
case MEM_PROT_READ | MEM_PROT_WRITE | MEM_PROT_EXEC: protect = PAGE_EXECUTE_READWRITE; break;
73
}
74
return protect;
75
}
76
77
#else
78
79
static uint32_t ConvertProtFlagsUnix(uint32_t flags) {
80
uint32_t protect = 0;
81
if (flags & MEM_PROT_READ)
82
protect |= PROT_READ;
83
if (flags & MEM_PROT_WRITE)
84
protect |= PROT_WRITE;
85
if (flags & MEM_PROT_EXEC)
86
protect |= PROT_EXEC;
87
return protect;
88
}
89
90
#endif
91
92
#if defined(_WIN32) && PPSSPP_ARCH(AMD64)
93
static uintptr_t last_executable_addr;
94
static void *SearchForFreeMem(size_t size) {
95
if (!last_executable_addr)
96
last_executable_addr = (uintptr_t) &hint_location - sys_info.dwPageSize;
97
last_executable_addr -= size;
98
99
MEMORY_BASIC_INFORMATION info;
100
while (VirtualQuery((void *)last_executable_addr, &info, sizeof(info)) == sizeof(info)) {
101
// went too far, unusable for executable memory
102
if (last_executable_addr + 0x80000000 < (uintptr_t) &hint_location)
103
return NULL;
104
105
uintptr_t end = last_executable_addr + size;
106
if (info.State != MEM_FREE)
107
{
108
last_executable_addr = (uintptr_t) info.AllocationBase - size;
109
continue;
110
}
111
112
if ((uintptr_t)info.BaseAddress + (uintptr_t)info.RegionSize >= end &&
113
(uintptr_t)info.BaseAddress <= last_executable_addr)
114
return (void *)last_executable_addr;
115
116
last_executable_addr -= size;
117
}
118
119
return NULL;
120
}
121
#endif
122
123
#if PPSSPP_PLATFORM(WINDOWS)
124
125
// This is purposely not a full wrapper for virtualalloc/mmap, but it
126
// provides exactly the primitive operations that PPSSPP needs.
127
void *AllocateExecutableMemory(size_t size) {
128
void *ptr = nullptr;
129
DWORD prot = PAGE_EXECUTE_READWRITE;
130
if (PlatformIsWXExclusive())
131
prot = PAGE_READWRITE;
132
if (sys_info.dwPageSize == 0)
133
GetSystemInfo(&sys_info);
134
#if PPSSPP_ARCH(AMD64)
135
if ((uintptr_t)&hint_location > 0xFFFFFFFFULL) {
136
size_t aligned_size = ppsspp_round_page(size);
137
#if 1 // Turn off to hunt for RIP bugs on x86-64.
138
ptr = SearchForFreeMem(aligned_size);
139
if (!ptr) {
140
// Let's try again, from the top.
141
// When we deallocate, this doesn't change, so we eventually run out of space.
142
last_executable_addr = 0;
143
ptr = SearchForFreeMem(aligned_size);
144
}
145
#endif
146
if (ptr) {
147
ptr = VirtualAlloc(ptr, aligned_size, MEM_RESERVE | MEM_COMMIT, prot);
148
} else {
149
WARN_LOG(Log::Common, "Unable to find nearby executable memory for jit. Proceeding with far memory.");
150
// Can still run, thanks to "RipAccessible".
151
ptr = VirtualAlloc(nullptr, aligned_size, MEM_RESERVE | MEM_COMMIT, prot);
152
}
153
}
154
else
155
#endif
156
{
157
#if PPSSPP_PLATFORM(UWP)
158
ptr = VirtualAllocFromApp(0, size, MEM_RESERVE | MEM_COMMIT, prot);
159
#else
160
ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, prot);
161
#endif
162
}
163
if (!ptr) {
164
ERROR_LOG(Log::MemMap, "Failed to allocate executable memory (%d)", (int)size);
165
}
166
return ptr;
167
}
168
169
#else // Non-Windows platforms
170
171
void *AllocateExecutableMemory(size_t size) {
172
static char *map_hint = nullptr;
173
174
#if PPSSPP_ARCH(AMD64)
175
// Try to request one that is close to our memory location if we're in high memory.
176
// We use a dummy global variable to give us a good location to start from.
177
// TODO: Should we also do this for ARM64?
178
if (!map_hint) {
179
if ((uintptr_t) &hint_location > 0xFFFFFFFFULL)
180
map_hint = (char*)ppsspp_round_page(&hint_location) - 0x20000000; // 0.5gb lower than our approximate location
181
else
182
map_hint = (char*)0x20000000; // 0.5GB mark in memory
183
} else if ((uintptr_t) map_hint > 0xFFFFFFFFULL) {
184
map_hint -= ppsspp_round_page(size); /* round down to the next page if we're in high memory */
185
}
186
#endif
187
188
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
189
if (PlatformIsWXExclusive())
190
prot = PROT_READ | PROT_WRITE; // POST_EXEC is added later in this case.
191
192
void* ptr = mmap(map_hint, size, prot, MAP_ANON | MAP_PRIVATE, -1, 0);
193
194
if (ptr == MAP_FAILED) {
195
ptr = nullptr;
196
ERROR_LOG(Log::MemMap, "Failed to allocate executable memory (%d) errno=%d", (int)size, errno);
197
}
198
199
#if PPSSPP_ARCH(AMD64)
200
else if ((uintptr_t)map_hint <= 0xFFFFFFFF) {
201
// Round up if we're below 32-bit mark, probably allocating sequentially.
202
map_hint += ppsspp_round_page(size);
203
204
// If we moved ahead too far, skip backwards and recalculate.
205
// When we free, we keep moving forward and eventually move too far.
206
if ((uintptr_t)map_hint - (uintptr_t) &hint_location >= 0x70000000) {
207
map_hint = 0;
208
}
209
}
210
#endif
211
212
return ptr;
213
}
214
215
#endif // non-windows
216
217
void *AllocateMemoryPages(size_t size, uint32_t memProtFlags) {
218
#ifdef _WIN32
219
if (sys_info.dwPageSize == 0)
220
GetSystemInfo(&sys_info);
221
uint32_t protect = ConvertProtFlagsWin32(memProtFlags);
222
// Make sure to do this after GetSystemInfo().
223
size = ppsspp_round_page(size);
224
#if PPSSPP_PLATFORM(UWP)
225
void* ptr = VirtualAllocFromApp(0, size, MEM_COMMIT, protect);
226
#else
227
void* ptr = VirtualAlloc(0, size, MEM_COMMIT, protect);
228
#endif
229
if (!ptr) {
230
ERROR_LOG(Log::MemMap, "Failed to allocate raw memory pages");
231
return nullptr;
232
}
233
#else
234
size = ppsspp_round_page(size);
235
uint32_t protect = ConvertProtFlagsUnix(memProtFlags);
236
void *ptr = mmap(0, size, protect, MAP_ANON | MAP_PRIVATE, -1, 0);
237
if (ptr == MAP_FAILED) {
238
ERROR_LOG(Log::MemMap, "Failed to allocate raw memory pages: errno=%d", errno);
239
return nullptr;
240
}
241
#endif
242
243
// printf("Mapped memory at %p (size %ld)\n", ptr,
244
// (unsigned long)size);
245
return ptr;
246
}
247
248
void *AllocateAlignedMemory(size_t size, size_t alignment) {
249
#ifdef _WIN32
250
void* ptr = _aligned_malloc(size, alignment);
251
#else
252
void* ptr = NULL;
253
#ifdef __ANDROID__
254
ptr = memalign(alignment, size);
255
#else
256
if (posix_memalign(&ptr, alignment, size) != 0) {
257
ptr = nullptr;
258
}
259
#endif
260
#endif
261
char temp[32];
262
NiceSizeFormat(size, temp, sizeof(temp));
263
_assert_msg_(ptr != nullptr, "Failed to allocate aligned memory of size %s (%llu)", temp, (unsigned long long)size);
264
return ptr;
265
}
266
267
void FreeMemoryPages(void *ptr, size_t size) {
268
if (!ptr)
269
return;
270
uintptr_t page_size = GetMemoryProtectPageSize();
271
size = (size + page_size - 1) & (~(page_size - 1));
272
#ifdef _WIN32
273
if (!VirtualFree(ptr, 0, MEM_RELEASE)) {
274
ERROR_LOG(Log::MemMap, "FreeMemoryPages failed!\n%s", GetLastErrorMsg().c_str());
275
}
276
#else
277
munmap(ptr, size);
278
#endif
279
}
280
281
void FreeExecutableMemory(void *ptr, size_t size) {
282
FreeMemoryPages(ptr, size);
283
}
284
285
void FreeAlignedMemory(void* ptr) {
286
if (!ptr)
287
return;
288
#ifdef _WIN32
289
_aligned_free(ptr);
290
#else
291
free(ptr);
292
#endif
293
}
294
295
bool PlatformIsWXExclusive() {
296
// Needed on platforms that disable W^X pages for security. Even without block linking, still should be much faster than IR JIT.
297
// This might also come in useful for UWP (Universal Windows Platform) if I'm understanding things correctly.
298
#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(UWP) || defined(__OpenBSD__)
299
return true;
300
#elif PPSSPP_PLATFORM(MAC) && PPSSPP_ARCH(ARM64)
301
return true;
302
#else
303
// Returning true here lets you test the W^X path on Windows and other non-W^X platforms.
304
return false;
305
#endif
306
}
307
308
bool ProtectMemoryPages(const void* ptr, size_t size, uint32_t memProtFlags) {
309
VERBOSE_LOG(Log::JIT, "ProtectMemoryPages: %p (%d) : r%d w%d x%d", ptr, (int)size,
310
(memProtFlags & MEM_PROT_READ) != 0, (memProtFlags & MEM_PROT_WRITE) != 0, (memProtFlags & MEM_PROT_EXEC) != 0);
311
312
if (PlatformIsWXExclusive()) {
313
if ((memProtFlags & (MEM_PROT_WRITE | MEM_PROT_EXEC)) == (MEM_PROT_WRITE | MEM_PROT_EXEC)) {
314
_assert_msg_(false, "Bad memory protect flags %d: W^X is in effect, can't both write and exec", memProtFlags);
315
}
316
}
317
// Note - VirtualProtect will affect the full pages containing the requested range.
318
// mprotect does not seem to, at least not on Android unless I made a mistake somewhere, so we manually round.
319
#ifdef _WIN32
320
uint32_t protect = ConvertProtFlagsWin32(memProtFlags);
321
322
#if PPSSPP_PLATFORM(UWP)
323
DWORD oldValue;
324
if (!VirtualProtectFromApp((void *)ptr, size, protect, &oldValue)) {
325
ERROR_LOG(Log::MemMap, "WriteProtectMemory failed!\n%s", GetLastErrorMsg().c_str());
326
return false;
327
}
328
#else
329
DWORD oldValue;
330
if (!VirtualProtect((void *)ptr, size, protect, &oldValue)) {
331
ERROR_LOG(Log::MemMap, "WriteProtectMemory failed!\n%s", GetLastErrorMsg().c_str());
332
return false;
333
}
334
#endif
335
return true;
336
#else
337
uint32_t protect = ConvertProtFlagsUnix(memProtFlags);
338
uintptr_t page_size = GetMemoryProtectPageSize();
339
340
uintptr_t start = (uintptr_t)ptr;
341
uintptr_t end = (uintptr_t)ptr + size;
342
start &= ~(page_size - 1);
343
end = (end + page_size - 1) & ~(page_size - 1);
344
int retval = mprotect((void *)start, end - start, protect);
345
if (retval != 0) {
346
ERROR_LOG(Log::MemMap, "mprotect failed (%p)! errno=%d (%s)", (void *)start, errno, strerror(errno));
347
return false;
348
}
349
return true;
350
#endif
351
}
352
353
int GetMemoryProtectPageSize() {
354
#ifdef _WIN32
355
if (sys_info.dwPageSize == 0)
356
GetSystemInfo(&sys_info);
357
return sys_info.dwPageSize;
358
#endif
359
return MEM_PAGE_SIZE;
360
}
361
#endif // !PPSSPP_PLATFORM(SWITCH)
362
363