diff options
Diffstat (limited to 'src/lj_alloc.c')
-rw-r--r-- | src/lj_alloc.c | 264 |
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. */ |
83 | typedef long (*PNTAVM)(HANDLE handle, void **addr, ULONG zbits, | 126 | typedef 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 | ||
92 | static void INIT_MMAP(void) | 135 | static 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. */ |
99 | static LJ_AINLINE void *CALL_MMAP(size_t size) | 143 | static 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 */ |
110 | static LJ_AINLINE void *DIRECT_MMAP(size_t size) | 154 | static 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 */ |
125 | static LJ_AINLINE void *CALL_MMAP(size_t size) | 167 | static 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 */ |
134 | static LJ_AINLINE void *DIRECT_MMAP(size_t size) | 176 | static 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 */ |
146 | static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size) | 188 | static 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 | ||
189 | static 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 | 236 | static 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 | ||
217 | static LJ_AINLINE void *CALL_MMAP(size_t size) | 248 | static 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" | 309 | static 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 | 338 | static void *CALL_MMAP(size_t size) | |
261 | /* 32 bit mode is easy. */ | ||
262 | static 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 | |||
351 | static 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 | ||
275 | static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size) | 359 | #endif |
360 | |||
361 | static 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. */ |
285 | static LJ_AINLINE void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz, | 371 | static 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 |