aboutsummaryrefslogtreecommitdiff
path: root/src/lj_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lj_alloc.c')
-rw-r--r--src/lj_alloc.c264
1 files changed, 179 insertions, 85 deletions
diff --git a/src/lj_alloc.c b/src/lj_alloc.c
index dc64dca9..33a2eb8f 100644
--- a/src/lj_alloc.c
+++ b/src/lj_alloc.c
@@ -72,13 +72,56 @@
72 72
73#define IS_DIRECT_BIT (SIZE_T_ONE) 73#define IS_DIRECT_BIT (SIZE_T_ONE)
74 74
75
76/* Determine system-specific block allocation method. */
75#if LJ_TARGET_WINDOWS 77#if LJ_TARGET_WINDOWS
76 78
77#define WIN32_LEAN_AND_MEAN 79#define WIN32_LEAN_AND_MEAN
78#include <windows.h> 80#include <windows.h>
79 81
82#define LJ_ALLOC_VIRTUALALLOC 1
83
84#if LJ_64 && !LJ_GC64
85#define LJ_ALLOC_NTAVM 1
86#endif
87
88#else
89
90#include <errno.h>
91/* If this include fails, then rebuild with: -DLUAJIT_USE_SYSMALLOC */
92#include <sys/mman.h>
93
94#define LJ_ALLOC_MMAP 1
95
80#if LJ_64 96#if LJ_64
81 97
98#define LJ_ALLOC_MMAP_PROBE 1
99
100#if LJ_GC64
101#define LJ_ALLOC_MBITS 47 /* 128 TB in LJ_GC64 mode. */
102#elif LJ_TARGET_X64 && LJ_HASJIT
103/* Due to limitations in the x64 compiler backend. */
104#define LJ_ALLOC_MBITS 31 /* 2 GB on x64 with !LJ_GC64. */
105#else
106#define LJ_ALLOC_MBITS 32 /* 4 GB on other archs with !LJ_GC64. */
107#endif
108
109#endif
110
111#if LJ_64 && !LJ_GC64 && defined(MAP_32BIT)
112#define LJ_ALLOC_MMAP32 1
113#endif
114
115#if LJ_TARGET_LINUX
116#define LJ_ALLOC_MREMAP 1
117#endif
118
119#endif
120
121
122#if LJ_ALLOC_VIRTUALALLOC
123
124#if LJ_ALLOC_NTAVM
82/* Undocumented, but hey, that's what we all love so much about Windows. */ 125/* Undocumented, but hey, that's what we all love so much about Windows. */
83typedef long (*PNTAVM)(HANDLE handle, void **addr, ULONG zbits, 126typedef long (*PNTAVM)(HANDLE handle, void **addr, ULONG zbits,
84 size_t *size, ULONG alloctype, ULONG prot); 127 size_t *size, ULONG alloctype, ULONG prot);
@@ -89,14 +132,15 @@ static PNTAVM ntavm;
89*/ 132*/
90#define NTAVM_ZEROBITS 1 133#define NTAVM_ZEROBITS 1
91 134
92static void INIT_MMAP(void) 135static void init_mmap(void)
93{ 136{
94 ntavm = (PNTAVM)GetProcAddress(GetModuleHandleA("ntdll.dll"), 137 ntavm = (PNTAVM)GetProcAddress(GetModuleHandleA("ntdll.dll"),
95 "NtAllocateVirtualMemory"); 138 "NtAllocateVirtualMemory");
96} 139}
140#define INIT_MMAP() init_mmap()
97 141
98/* Win64 32 bit MMAP via NtAllocateVirtualMemory. */ 142/* Win64 32 bit MMAP via NtAllocateVirtualMemory. */
99static LJ_AINLINE void *CALL_MMAP(size_t size) 143static void *CALL_MMAP(size_t size)
100{ 144{
101 DWORD olderr = GetLastError(); 145 DWORD olderr = GetLastError();
102 void *ptr = NULL; 146 void *ptr = NULL;
@@ -107,7 +151,7 @@ static LJ_AINLINE void *CALL_MMAP(size_t size)
107} 151}
108 152
109/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ 153/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
110static LJ_AINLINE void *DIRECT_MMAP(size_t size) 154static void *DIRECT_MMAP(size_t size)
111{ 155{
112 DWORD olderr = GetLastError(); 156 DWORD olderr = GetLastError();
113 void *ptr = NULL; 157 void *ptr = NULL;
@@ -119,23 +163,21 @@ static LJ_AINLINE void *DIRECT_MMAP(size_t size)
119 163
120#else 164#else
121 165
122#define INIT_MMAP() ((void)0)
123
124/* Win32 MMAP via VirtualAlloc */ 166/* Win32 MMAP via VirtualAlloc */
125static LJ_AINLINE void *CALL_MMAP(size_t size) 167static void *CALL_MMAP(size_t size)
126{ 168{
127 DWORD olderr = GetLastError(); 169 DWORD olderr = GetLastError();
128 void *ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 170 void *ptr = LJ_WIN_VALLOC(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
129 SetLastError(olderr); 171 SetLastError(olderr);
130 return ptr ? ptr : MFAIL; 172 return ptr ? ptr : MFAIL;
131} 173}
132 174
133/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ 175/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
134static LJ_AINLINE void *DIRECT_MMAP(size_t size) 176static void *DIRECT_MMAP(size_t size)
135{ 177{
136 DWORD olderr = GetLastError(); 178 DWORD olderr = GetLastError();
137 void *ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, 179 void *ptr = LJ_WIN_VALLOC(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
138 PAGE_READWRITE); 180 PAGE_READWRITE);
139 SetLastError(olderr); 181 SetLastError(olderr);
140 return ptr ? ptr : MFAIL; 182 return ptr ? ptr : MFAIL;
141} 183}
@@ -143,7 +185,7 @@ static LJ_AINLINE void *DIRECT_MMAP(size_t size)
143#endif 185#endif
144 186
145/* This function supports releasing coalesed segments */ 187/* This function supports releasing coalesed segments */
146static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size) 188static int CALL_MUNMAP(void *ptr, size_t size)
147{ 189{
148 DWORD olderr = GetLastError(); 190 DWORD olderr = GetLastError();
149 MEMORY_BASIC_INFORMATION minfo; 191 MEMORY_BASIC_INFORMATION minfo;
@@ -163,10 +205,7 @@ static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size)
163 return 0; 205 return 0;
164} 206}
165 207
166#else 208#elif LJ_ALLOC_MMAP
167
168#include <errno.h>
169#include <sys/mman.h>
170 209
171#define MMAP_PROT (PROT_READ|PROT_WRITE) 210#define MMAP_PROT (PROT_READ|PROT_WRITE)
172#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) 211#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
@@ -174,105 +213,152 @@ static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size)
174#endif 213#endif
175#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) 214#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS)
176 215
177#if LJ_64 216#if LJ_ALLOC_MMAP_PROBE
178/* 64 bit mode needs special support for allocating memory in the lower 2GB. */
179
180#if defined(MAP_32BIT)
181 217
182#if defined(__sun__) 218#ifdef MAP_TRYFIXED
183#define MMAP_REGION_START ((uintptr_t)0x1000) 219#define MMAP_FLAGS_PROBE (MMAP_FLAGS|MAP_TRYFIXED)
184#else 220#else
185/* Actually this only gives us max. 1GB in current Linux kernels. */ 221#define MMAP_FLAGS_PROBE MMAP_FLAGS
186#define MMAP_REGION_START ((uintptr_t)0)
187#endif 222#endif
188 223
189static LJ_AINLINE void *CALL_MMAP(size_t size) 224#define LJ_ALLOC_MMAP_PROBE_MAX 30
190{ 225#define LJ_ALLOC_MMAP_PROBE_LINEAR 5
191 int olderr = errno;
192 void *ptr = mmap((void *)MMAP_REGION_START, size, MMAP_PROT, MAP_32BIT|MMAP_FLAGS, -1, 0);
193 errno = olderr;
194 return ptr;
195}
196 226
197#elif LJ_TARGET_OSX || LJ_TARGET_PS4 || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun__) || LJ_TARGET_CYGWIN 227#define LJ_ALLOC_MMAP_PROBE_LOWER ((uintptr_t)0x4000)
198 228
199/* OSX and FreeBSD mmap() use a naive first-fit linear search. 229/* No point in a giant ifdef mess. Just try to open /dev/urandom.
200** That's perfect for us. Except that -pagezero_size must be set for OSX, 230** It doesn't really matter if this fails, since we get some ASLR bits from
201** otherwise the lower 4GB are blocked. And the 32GB RLIMIT_DATA needs 231** every unsuitable allocation, too. And we prefer linear allocation, anyway.
202** to be reduced to 250MB on FreeBSD.
203*/ 232*/
204#if LJ_TARGET_OSX || defined(__DragonFly__) 233#include <fcntl.h>
205#define MMAP_REGION_START ((uintptr_t)0x10000) 234#include <unistd.h>
206#elif LJ_TARGET_PS4
207#define MMAP_REGION_START ((uintptr_t)0x4000)
208#else
209#define MMAP_REGION_START ((uintptr_t)0x10000000)
210#endif
211#define MMAP_REGION_END ((uintptr_t)0x80000000)
212 235
213#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && !LJ_TARGET_PS4 236static uintptr_t mmap_probe_seed(void)
214#include <sys/resource.h> 237{
215#endif 238 uintptr_t val;
239 int fd = open("/dev/urandom", O_RDONLY);
240 if (fd != -1) {
241 int ok = ((size_t)read(fd, &val, sizeof(val)) == sizeof(val));
242 (void)close(fd);
243 if (ok) return val;
244 }
245 return 1; /* Punt. */
246}
216 247
217static LJ_AINLINE void *CALL_MMAP(size_t size) 248static void *mmap_probe(size_t size)
218{ 249{
219 int olderr = errno;
220 /* Hint for next allocation. Doesn't need to be thread-safe. */ 250 /* Hint for next allocation. Doesn't need to be thread-safe. */
221 static uintptr_t alloc_hint = MMAP_REGION_START; 251 static uintptr_t hint_addr = 0;
222 int retry = 0; 252 static uintptr_t hint_prng = 0;
223#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && !LJ_TARGET_PS4 253 int olderr = errno;
224 static int rlimit_modified = 0; 254 int retry;
225 if (LJ_UNLIKELY(rlimit_modified == 0)) { 255 for (retry = 0; retry < LJ_ALLOC_MMAP_PROBE_MAX; retry++) {
226 struct rlimit rlim; 256 void *p = mmap((void *)hint_addr, size, MMAP_PROT, MMAP_FLAGS_PROBE, -1, 0);
227 rlim.rlim_cur = rlim.rlim_max = MMAP_REGION_START; 257 uintptr_t addr = (uintptr_t)p;
228 setrlimit(RLIMIT_DATA, &rlim); /* Ignore result. May fail below. */ 258 if ((addr >> LJ_ALLOC_MBITS) == 0 && addr >= LJ_ALLOC_MMAP_PROBE_LOWER &&
229 rlimit_modified = 1; 259 ((addr + size) >> LJ_ALLOC_MBITS) == 0) {
230 } 260 /* We got a suitable address. Bump the hint address. */
231#endif 261 hint_addr = addr + size;
232 for (;;) {
233 void *p = mmap((void *)alloc_hint, size, MMAP_PROT, MMAP_FLAGS, -1, 0);
234 if ((uintptr_t)p >= MMAP_REGION_START &&
235 (uintptr_t)p + size < MMAP_REGION_END) {
236 alloc_hint = (uintptr_t)p + size;
237 errno = olderr; 262 errno = olderr;
238 return p; 263 return p;
239 } 264 }
240 if (p != CMFAIL) munmap(p, size); 265 if (p != MFAIL) {
241#if defined(__sun__) || defined(__DragonFly__) 266 munmap(p, size);
242 alloc_hint += 0x1000000; /* Need near-exhaustive linear scan. */ 267 } else if (errno == ENOMEM) {
243 if (alloc_hint + size < MMAP_REGION_END) continue; 268 return MFAIL;
244#endif 269 }
245 if (retry) break; 270 if (hint_addr) {
246 retry = 1; 271 /* First, try linear probing. */
247 alloc_hint = MMAP_REGION_START; 272 if (retry < LJ_ALLOC_MMAP_PROBE_LINEAR) {
273 hint_addr += 0x1000000;
274 if (((hint_addr + size) >> LJ_ALLOC_MBITS) != 0)
275 hint_addr = 0;
276 continue;
277 } else if (retry == LJ_ALLOC_MMAP_PROBE_LINEAR) {
278 /* Next, try a no-hint probe to get back an ASLR address. */
279 hint_addr = 0;
280 continue;
281 }
282 }
283 /* Finally, try pseudo-random probing. */
284 if (LJ_UNLIKELY(hint_prng == 0)) {
285 hint_prng = mmap_probe_seed();
286 }
287 /* The unsuitable address we got has some ASLR PRNG bits. */
288 hint_addr ^= addr & ~((uintptr_t)(LJ_PAGESIZE-1));
289 do { /* The PRNG itself is very weak, but see above. */
290 hint_prng = hint_prng * 1103515245 + 12345;
291 hint_addr ^= hint_prng * (uintptr_t)LJ_PAGESIZE;
292 hint_addr &= (((uintptr_t)1 << LJ_ALLOC_MBITS)-1);
293 } while (hint_addr < LJ_ALLOC_MMAP_PROBE_LOWER);
248 } 294 }
249 errno = olderr; 295 errno = olderr;
250 return CMFAIL; 296 return MFAIL;
251} 297}
252 298
299#endif
300
301#if LJ_ALLOC_MMAP32
302
303#if defined(__sun__)
304#define LJ_ALLOC_MMAP32_START ((uintptr_t)0x1000)
253#else 305#else
306#define LJ_ALLOC_MMAP32_START ((uintptr_t)0)
307#endif
254 308
255#error "NYI: need an equivalent of MAP_32BIT for this 64 bit OS" 309static void *mmap_map32(size_t size)
310{
311#if LJ_ALLOC_MMAP_PROBE
312 static int fallback = 0;
313 if (fallback)
314 return mmap_probe(size);
315#endif
316 {
317 int olderr = errno;
318 void *ptr = mmap((void *)LJ_ALLOC_MMAP32_START, size, MMAP_PROT, MAP_32BIT|MMAP_FLAGS, -1, 0);
319 errno = olderr;
320 /* This only allows 1GB on Linux. So fallback to probing to get 2GB. */
321#if LJ_ALLOC_MMAP_PROBE
322 if (ptr == MFAIL) {
323 fallback = 1;
324 return mmap_probe(size);
325 }
326#endif
327 return ptr;
328 }
329}
256 330
257#endif 331#endif
258 332
333#if LJ_ALLOC_MMAP32
334#define CALL_MMAP(size) mmap_map32(size)
335#elif LJ_ALLOC_MMAP_PROBE
336#define CALL_MMAP(size) mmap_probe(size)
259#else 337#else
260 338static void *CALL_MMAP(size_t size)
261/* 32 bit mode is easy. */
262static LJ_AINLINE void *CALL_MMAP(size_t size)
263{ 339{
264 int olderr = errno; 340 int olderr = errno;
265 void *ptr = mmap(NULL, size, MMAP_PROT, MMAP_FLAGS, -1, 0); 341 void *ptr = mmap(NULL, size, MMAP_PROT, MMAP_FLAGS, -1, 0);
266 errno = olderr; 342 errno = olderr;
267 return ptr; 343 return ptr;
268} 344}
269
270#endif 345#endif
271 346
272#define INIT_MMAP() ((void)0) 347#if LJ_64 && !LJ_GC64 && ((defined(__FreeBSD__) && __FreeBSD__ < 10) || defined(__FreeBSD_kernel__)) && !LJ_TARGET_PS4
273#define DIRECT_MMAP(s) CALL_MMAP(s) 348
349#include <sys/resource.h>
350
351static void init_mmap(void)
352{
353 struct rlimit rlim;
354 rlim.rlim_cur = rlim.rlim_max = 0x10000;
355 setrlimit(RLIMIT_DATA, &rlim); /* Ignore result. May fail later. */
356}
357#define INIT_MMAP() init_mmap()
274 358
275static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size) 359#endif
360
361static int CALL_MUNMAP(void *ptr, size_t size)
276{ 362{
277 int olderr = errno; 363 int olderr = errno;
278 int ret = munmap(ptr, size); 364 int ret = munmap(ptr, size);
@@ -280,10 +366,9 @@ static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size)
280 return ret; 366 return ret;
281} 367}
282 368
283#if LJ_TARGET_LINUX 369#if LJ_ALLOC_MREMAP
284/* Need to define _GNU_SOURCE to get the mremap prototype. */ 370/* Need to define _GNU_SOURCE to get the mremap prototype. */
285static LJ_AINLINE void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz, 371static void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz, int flags)
286 int flags)
287{ 372{
288 int olderr = errno; 373 int olderr = errno;
289 ptr = mremap(ptr, osz, nsz, flags); 374 ptr = mremap(ptr, osz, nsz, flags);
@@ -294,7 +379,7 @@ static LJ_AINLINE void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz,
294#define CALL_MREMAP(addr, osz, nsz, mv) CALL_MREMAP_((addr), (osz), (nsz), (mv)) 379#define CALL_MREMAP(addr, osz, nsz, mv) CALL_MREMAP_((addr), (osz), (nsz), (mv))
295#define CALL_MREMAP_NOMOVE 0 380#define CALL_MREMAP_NOMOVE 0
296#define CALL_MREMAP_MAYMOVE 1 381#define CALL_MREMAP_MAYMOVE 1
297#if LJ_64 382#if LJ_64 && !LJ_GC64
298#define CALL_MREMAP_MV CALL_MREMAP_NOMOVE 383#define CALL_MREMAP_MV CALL_MREMAP_NOMOVE
299#else 384#else
300#define CALL_MREMAP_MV CALL_MREMAP_MAYMOVE 385#define CALL_MREMAP_MV CALL_MREMAP_MAYMOVE
@@ -303,6 +388,15 @@ static LJ_AINLINE void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz,
303 388
304#endif 389#endif
305 390
391
392#ifndef INIT_MMAP
393#define INIT_MMAP() ((void)0)
394#endif
395
396#ifndef DIRECT_MMAP
397#define DIRECT_MMAP(s) CALL_MMAP(s)
398#endif
399
306#ifndef CALL_MREMAP 400#ifndef CALL_MREMAP
307#define CALL_MREMAP(addr, osz, nsz, mv) ((void)osz, MFAIL) 401#define CALL_MREMAP(addr, osz, nsz, mv) ((void)osz, MFAIL)
308#endif 402#endif