Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include "llvm-version.h"
4 : : #include "platform.h"
5 : :
6 : : #include <llvm/ExecutionEngine/SectionMemoryManager.h>
7 : : #include "julia.h"
8 : : #include "julia_internal.h"
9 : :
10 : : #ifdef _OS_LINUX_
11 : : # include <sys/syscall.h>
12 : : # include <sys/utsname.h>
13 : : # include <sys/resource.h>
14 : : #endif
15 : : #ifndef _OS_WINDOWS_
16 : : # include <sys/mman.h>
17 : : # include <sys/stat.h>
18 : : # include <fcntl.h>
19 : : # include <unistd.h>
20 : : # if defined(_OS_DARWIN_) && !defined(MAP_ANONYMOUS)
21 : : # define MAP_ANONYMOUS MAP_ANON
22 : : # endif
23 : : #endif
24 : : #ifdef _OS_FREEBSD_
25 : : # include <sys/types.h>
26 : : # include <sys/resource.h>
27 : : #endif
28 : : #include "julia_assert.h"
29 : :
30 : : namespace {
31 : :
32 : 2585 : static size_t get_block_size(size_t size)
33 : : {
34 [ - + ]: 2585 : return (size > jl_page_size * 256 ? LLT_ALIGN(size, jl_page_size) :
35 : 2585 : jl_page_size * 256);
36 : : }
37 : :
38 : : // Wrapper function to mmap/munmap/mprotect pages...
39 : 2585 : static void *map_anon_page(size_t size)
40 : : {
41 : : #ifdef _OS_WINDOWS_
42 : : char *mem = (char*)VirtualAlloc(NULL, size + jl_page_size,
43 : : MEM_COMMIT, PAGE_READWRITE);
44 : : assert(mem && "Cannot allocate RW memory");
45 : : mem = (char*)LLT_ALIGN(uintptr_t(mem), jl_page_size);
46 : : #else // _OS_WINDOWS_
47 : 2585 : void *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE,
48 : : MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
49 [ - + ]: 2585 : assert(mem != MAP_FAILED && "Cannot allocate RW memory");
50 : : #endif // _OS_WINDOWS_
51 : 2585 : return mem;
52 : : }
53 : :
54 : 96 : static void unmap_page(void *ptr, size_t size)
55 : : {
56 : : #ifdef _OS_WINDOWS_
57 : : VirtualFree(ptr, size, MEM_DECOMMIT);
58 : : #else // _OS_WINDOWS_
59 : 96 : munmap(ptr, size);
60 : : #endif // _OS_WINDOWS_
61 : 96 : }
62 : :
63 : : #ifdef _OS_WINDOWS_
64 : : enum class Prot : int {
65 : : RW = PAGE_READWRITE,
66 : : RX = PAGE_EXECUTE,
67 : : RO = PAGE_READONLY,
68 : : NO = PAGE_NOACCESS
69 : : };
70 : :
71 : : static void protect_page(void *ptr, size_t size, Prot flags)
72 : : {
73 : : DWORD old_prot;
74 : : if (!VirtualProtect(ptr, size, (DWORD)flags, &old_prot)) {
75 : : jl_safe_printf("Cannot protect page @%p of size %u to 0x%x (err 0x%x)\n",
76 : : ptr, (unsigned)size, (unsigned)flags,
77 : : (unsigned)GetLastError());
78 : : abort();
79 : : }
80 : : }
81 : : #else // _OS_WINDOWS_
82 : : enum class Prot : int {
83 : : RW = PROT_READ | PROT_WRITE,
84 : : RX = PROT_READ | PROT_EXEC,
85 : : RO = PROT_READ,
86 : : NO = PROT_NONE
87 : : };
88 : :
89 : 1406 : static void protect_page(void *ptr, size_t size, Prot flags)
90 : : {
91 : 1406 : int ret = mprotect(ptr, size, (int)flags);
92 [ - + ]: 1406 : if (ret != 0) {
93 : 0 : perror(__func__);
94 : 0 : abort();
95 : : }
96 : 1406 : }
97 : :
98 : 0 : static bool check_fd_or_close(int fd)
99 : : {
100 [ # # ]: 0 : if (fd == -1)
101 : 0 : return false;
102 : 0 : int err = fcntl(fd, F_SETFD, FD_CLOEXEC);
103 [ # # ]: 0 : assert(err == 0);
104 : : (void)err; // prevent compiler warning
105 [ # # # # : 0 : if (fchmod(fd, S_IRWXU) != 0 ||
# # ]
106 : 0 : ftruncate(fd, jl_page_size) != 0) {
107 : 0 : close(fd);
108 : 0 : return false;
109 : : }
110 : : // This can fail due to `noexec` mount option ....
111 : 0 : void *ptr = mmap(nullptr, jl_page_size, PROT_READ | PROT_EXEC,
112 : : MAP_SHARED, fd, 0);
113 [ # # ]: 0 : if (ptr == MAP_FAILED) {
114 : 0 : close(fd);
115 : 0 : return false;
116 : : }
117 : 0 : munmap(ptr, jl_page_size);
118 : 0 : return true;
119 : : }
120 : : #endif // _OS_WINDOWS_
121 : :
122 : : static intptr_t anon_hdl = -1;
123 : :
124 : : #ifdef _OS_WINDOWS_
125 : : // As far as I can tell `CreateFileMapping` cannot be resized on windows.
126 : : // Also, creating big file mapping and then map pieces of it seems to
127 : : // consume too much global resources. Therefore, we use each file mapping
128 : : // as a block on windows
129 : : static void *create_shared_map(size_t size, size_t id)
130 : : {
131 : : void *addr = MapViewOfFile((HANDLE)id, FILE_MAP_ALL_ACCESS,
132 : : 0, 0, size);
133 : : assert(addr && "Cannot map RW view");
134 : : return addr;
135 : : }
136 : :
137 : : static intptr_t init_shared_map()
138 : : {
139 : : anon_hdl = 0;
140 : : return 0;
141 : : }
142 : :
143 : : static void *alloc_shared_page(size_t size, size_t *id, bool exec)
144 : : {
145 : : assert(size % jl_page_size == 0);
146 : : DWORD file_mode = exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
147 : : HANDLE hdl = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
148 : : file_mode, 0, size, NULL);
149 : : *id = (size_t)hdl;
150 : : // We set the maximum permissions for this to the maximum for this file, and then
151 : : // VirtualProtect, such that the debugger can still access these
152 : : // pages and set breakpoints if it wants to.
153 : : DWORD map_mode = FILE_MAP_ALL_ACCESS | (exec ? FILE_MAP_EXECUTE : 0);
154 : : void *addr = MapViewOfFile(hdl, map_mode, 0, 0, size);
155 : : assert(addr && "Cannot map RO view");
156 : : DWORD protect_mode = exec ? PAGE_EXECUTE_READ : PAGE_READONLY;
157 : : VirtualProtect(addr, size, protect_mode, &file_mode);
158 : : return addr;
159 : : }
160 : : #else // _OS_WINDOWS_
161 : : // For shared mapped region
162 : 0 : static intptr_t get_anon_hdl(void)
163 : : {
164 : 0 : int fd = -1;
165 : :
166 : : // Linux and FreeBSD can create an anonymous fd without touching the
167 : : // file system.
168 : : # ifdef __NR_memfd_create
169 : 0 : fd = syscall(__NR_memfd_create, "julia-codegen", 0);
170 [ # # ]: 0 : if (check_fd_or_close(fd))
171 : 0 : return fd;
172 : : # endif
173 : : # ifdef _OS_FREEBSD_
174 : : fd = shm_open(SHM_ANON, O_RDWR, S_IRWXU);
175 : : if (check_fd_or_close(fd))
176 : : return fd;
177 : : # endif
178 : 0 : char shm_name[JL_PATH_MAX] = "julia-codegen-0123456789-0123456789/tmp///";
179 : 0 : pid_t pid = getpid();
180 : : // `shm_open` can't be mapped exec on mac
181 : : # ifndef _OS_DARWIN_
182 : 0 : do {
183 : 0 : snprintf(shm_name, sizeof(shm_name),
184 : : "julia-codegen-%d-%d", (int)pid, rand());
185 : 0 : fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRWXU);
186 [ # # ]: 0 : if (check_fd_or_close(fd)) {
187 : 0 : shm_unlink(shm_name);
188 : 0 : return fd;
189 : : }
190 [ # # ]: 0 : } while (errno == EEXIST);
191 : : # endif
192 : 0 : FILE *tmpf = tmpfile();
193 [ # # ]: 0 : if (tmpf) {
194 : 0 : fd = dup(fileno(tmpf));
195 : 0 : fclose(tmpf);
196 [ # # ]: 0 : if (check_fd_or_close(fd)) {
197 : 0 : return fd;
198 : : }
199 : : }
200 : 0 : size_t len = sizeof(shm_name);
201 [ # # ]: 0 : if (uv_os_tmpdir(shm_name, &len) != 0) {
202 : : // Unknown error; default to `/tmp`
203 : 0 : snprintf(shm_name, sizeof(shm_name), "/tmp");
204 : 0 : len = 4;
205 : : }
206 : 0 : snprintf(shm_name + len, sizeof(shm_name) - len,
207 : : "/julia-codegen-%d-XXXXXX", (int)pid);
208 : 0 : fd = mkstemp(shm_name);
209 [ # # ]: 0 : if (check_fd_or_close(fd)) {
210 : 0 : unlink(shm_name);
211 : 0 : return fd;
212 : : }
213 : 0 : return -1;
214 : : }
215 : :
216 : : static _Atomic(size_t) map_offset{0};
217 : : // Multiple of 128MB.
218 : : // Hopefully no one will set a ulimit for this to be a problem...
219 : : static constexpr size_t map_size_inc_default = 128 * 1024 * 1024;
220 : : static size_t map_size = 0;
221 : : static uv_mutex_t shared_map_lock;
222 : :
223 : 0 : static size_t get_map_size_inc()
224 : : {
225 : : rlimit rl;
226 [ # # ]: 0 : if (getrlimit(RLIMIT_FSIZE, &rl) != -1) {
227 [ # # ]: 0 : if (rl.rlim_cur != RLIM_INFINITY) {
228 : 0 : return std::min<size_t>(map_size_inc_default, rl.rlim_cur);
229 : : }
230 [ # # ]: 0 : if (rl.rlim_max != RLIM_INFINITY) {
231 : 0 : return std::min<size_t>(map_size_inc_default, rl.rlim_max);
232 : : }
233 : : }
234 : 0 : return map_size_inc_default;
235 : : }
236 : :
237 : 0 : static void *create_shared_map(size_t size, size_t id)
238 : : {
239 : 0 : void *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
240 : : anon_hdl, id);
241 [ # # ]: 0 : assert(addr != MAP_FAILED && "Cannot map RW view");
242 : 0 : return addr;
243 : : }
244 : :
245 : 0 : static intptr_t init_shared_map()
246 : : {
247 : 0 : anon_hdl = get_anon_hdl();
248 [ # # ]: 0 : if (anon_hdl == -1)
249 : 0 : return -1;
250 : 0 : jl_atomic_store_relaxed(&map_offset, 0);
251 : 0 : map_size = get_map_size_inc();
252 : 0 : int ret = ftruncate(anon_hdl, map_size);
253 [ # # ]: 0 : if (ret != 0) {
254 : 0 : perror(__func__);
255 : 0 : abort();
256 : : }
257 : 0 : return anon_hdl;
258 : : }
259 : :
260 : 0 : static void *alloc_shared_page(size_t size, size_t *id, bool exec)
261 : : {
262 [ # # ]: 0 : assert(size % jl_page_size == 0);
263 : 0 : size_t off = jl_atomic_fetch_add(&map_offset, size);
264 : 0 : *id = off;
265 : 0 : size_t map_size_inc = get_map_size_inc();
266 [ # # ]: 0 : if (__unlikely(off + size > map_size)) {
267 : 0 : uv_mutex_lock(&shared_map_lock);
268 : 0 : size_t old_size = map_size;
269 [ # # ]: 0 : while (off + size > map_size)
270 : 0 : map_size += map_size_inc;
271 [ # # ]: 0 : if (old_size != map_size) {
272 : 0 : int ret = ftruncate(anon_hdl, map_size);
273 [ # # ]: 0 : if (ret != 0) {
274 : 0 : perror(__func__);
275 : 0 : abort();
276 : : }
277 : : }
278 : 0 : uv_mutex_unlock(&shared_map_lock);
279 : : }
280 : 0 : return create_shared_map(size, off);
281 : : }
282 : : #endif // _OS_WINDOWS_
283 : :
284 : : #ifdef _OS_LINUX_
285 : : // Using `/proc/self/mem`, A.K.A. Keno's remote memory manager.
286 : :
287 : 784407 : ssize_t pwrite_addr(int fd, const void *buf, size_t nbyte, uintptr_t addr)
288 : : {
289 : : static_assert(sizeof(off_t) >= 8, "off_t is smaller than 64bits");
290 : : #ifdef _P64
291 : 784407 : const uintptr_t sign_bit = uintptr_t(1) << 63;
292 [ - + ]: 784407 : if (__unlikely(sign_bit & addr)) {
293 : : // This case should not happen with default kernel on 64bit since the address belongs
294 : : // to kernel space (linear mapping).
295 : : // However, it seems possible to change this at kernel compile time.
296 : :
297 : : // pwrite doesn't support offset with sign bit set but lseek does.
298 : : // This is obviously not thread safe but none of the mem manager does anyway...
299 : : // From the kernel code, `lseek` with `SEEK_SET` can't fail.
300 : : // However, this can possibly confuse the glibc wrapper to think that
301 : : // we have invalid input value. Use syscall directly to be sure.
302 : 0 : syscall(SYS_lseek, (long)fd, addr, (long)SEEK_SET);
303 : : // The return value can be -1 when the glibc syscall function
304 : : // think we have an error return with and `addr` that's too large.
305 : : // Ignore the return value for now.
306 : 0 : return write(fd, buf, nbyte);
307 : : }
308 : : #endif
309 : 784407 : return pwrite(fd, buf, nbyte, (off_t)addr);
310 : : }
311 : :
312 : : // Do not call this directly.
313 : : // Use `get_self_mem_fd` which has a guard to call this only once.
314 : 567 : static int _init_self_mem()
315 : : {
316 : 567 : uv_mutex_init(&shared_map_lock);
317 : : struct utsname kernel;
318 : 567 : uname(&kernel);
319 : : int major, minor;
320 [ - + ]: 567 : if (-1 == sscanf(kernel.release, "%d.%d", &major, &minor))
321 : 0 : return -1;
322 : : // Can't risk getting a memory block backed by transparent huge pages,
323 : : // which cause the kernel to freeze on systems that have the DirtyCOW
324 : : // mitigation patch, but are < 4.10.
325 [ - + - - : 567 : if (!(major > 4 || (major == 4 && minor >= 10)))
- - ]
326 : 0 : return -1;
327 : : #ifdef O_CLOEXEC
328 : 567 : int fd = open("/proc/self/mem", O_RDWR | O_SYNC | O_CLOEXEC);
329 [ - + ]: 567 : if (fd == -1)
330 : 0 : return -1;
331 : : #else
332 : : int fd = open("/proc/self/mem", O_RDWR | O_SYNC);
333 : : if (fd == -1)
334 : : return -1;
335 : : fcntl(fd, F_SETFD, FD_CLOEXEC);
336 : : #endif
337 : :
338 : : // Check if we can write to a RX page
339 : 567 : void *test_pg = mmap(nullptr, jl_page_size, PROT_READ | PROT_EXEC,
340 : : MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
341 : : // We can ignore this though failure to allocate executable memory would be a bigger problem.
342 [ - + ]: 567 : assert(test_pg != MAP_FAILED && "Cannot allocate executable memory");
343 : :
344 : 567 : const uint64_t v = 0xffff000012345678u;
345 : 567 : int ret = pwrite_addr(fd, (const void*)&v, sizeof(uint64_t), (uintptr_t)test_pg);
346 [ + - - + : 567 : if (ret != sizeof(uint64_t) || *(volatile uint64_t*)test_pg != v) {
- + ]
347 : 0 : munmap(test_pg, jl_page_size);
348 : 0 : close(fd);
349 : 0 : return -1;
350 : : }
351 : 567 : munmap(test_pg, jl_page_size);
352 : 567 : return fd;
353 : : }
354 : :
355 : 785541 : static int get_self_mem_fd()
356 : : {
357 [ + + + - ]: 785541 : static int fd = _init_self_mem();
358 : 785541 : return fd;
359 : : }
360 : :
361 : 783840 : static void write_self_mem(void *dest, void *ptr, size_t size)
362 : : {
363 [ + - ]: 783840 : while (size > 0) {
364 : 783840 : ssize_t ret = pwrite_addr(get_self_mem_fd(), ptr, size, (uintptr_t)dest);
365 [ + - ]: 783840 : if ((size_t)ret == size)
366 : 783840 : return;
367 [ # # # # : 0 : if (ret == -1 && (errno == EAGAIN || errno == EINTR))
# # ]
368 : 0 : continue;
369 [ # # ]: 0 : assert((size_t)ret < size);
370 : 0 : size -= ret;
371 : 0 : ptr = (char*)ptr + ret;
372 : 0 : dest = (char*)dest + ret;
373 : : }
374 : : }
375 : : #endif // _OS_LINUX_
376 : :
377 : : using namespace llvm;
378 : :
379 : : // Allocation strategies
380 : : // * For RW data, no memory protection needed, use plain memory pool.
381 : : // * For RO data or code,
382 : : //
383 : : // The first allocation in the page always has write address equals to
384 : : // runtime address.
385 : : //
386 : : // 1. shared dual map
387 : : //
388 : : // Map an (unlinked) anonymous file as memory pool.
389 : : // After first allocation, write address points to the second map.
390 : : // The second map is set to unreadable and unwritable in finalization.
391 : : //
392 : : // 2. private dual map
393 : : //
394 : : // Same as above but use anonymous memory map as memory pool,
395 : : // and use low level OS api to set up the second map.
396 : : //
397 : : // 3. copying data into RO page bypassing page protection
398 : : //
399 : : // After first allocation, write address points to a temporary buffer.
400 : : // Requires copying data out of the temporary buffer in finalization.
401 : :
402 : : // Allocates at least 256 pages per block and keep up to 8 blocks in the free
403 : : // list. The block with the least free space is discarded when we need to
404 : : // allocate a new page.
405 : : // Unused full pages are free'd from the block before discarding so at most
406 : : // one page is wasted on each discarded blocks. There should be at most one
407 : : // block with more than 128 pages available so the discarded one must have
408 : : // less than 128 pages available and therefore at least 128 pages used.
409 : : // (Apart from fragmentation) this guarantees less than 1% of memory is wasted.
410 : :
411 : : // the `shared` type parameter is for Windows only....
412 : : struct Block {
413 : : // runtime address
414 : : char *ptr{nullptr};
415 : : size_t total{0};
416 : : size_t avail{0};
417 : :
418 : : Block(const Block&) = delete;
419 : : Block &operator=(const Block&) = delete;
420 : 0 : Block(Block &&other)
421 : 0 : : ptr(other.ptr),
422 : 0 : total(other.total),
423 : 0 : avail(other.avail)
424 : : {
425 : 0 : other.ptr = nullptr;
426 : 0 : other.total = other.avail = 0;
427 : 0 : }
428 : :
429 : 1398 : Block() = default;
430 : :
431 : 1572660 : void *alloc(size_t size, size_t align)
432 : : {
433 : 1572660 : size_t aligned_avail = avail & (-align);
434 [ + + ]: 1572660 : if (aligned_avail < size)
435 : 338 : return nullptr;
436 : 1572320 : char *p = ptr + total - aligned_avail;
437 : 1572320 : avail = aligned_avail - size;
438 : 1572320 : return p;
439 : : }
440 : 3991 : void reset(void *addr, size_t size)
441 : : {
442 [ + + ]: 3991 : if (avail >= jl_page_size) {
443 : 96 : uintptr_t end = uintptr_t(ptr) + total;
444 : 96 : uintptr_t first_free = end - avail;
445 : 96 : first_free = LLT_ALIGN(first_free, jl_page_size);
446 [ - + ]: 96 : assert(first_free < end);
447 : 96 : unmap_page((void*)first_free, end - first_free);
448 : : }
449 : 3991 : ptr = (char*)addr;
450 : 3991 : total = avail = size;
451 : 3991 : }
452 : : };
453 : :
454 : : class RWAllocator {
455 : : static constexpr int nblocks = 8;
456 : : Block blocks[nblocks]{};
457 : : public:
458 : 3230 : void *alloc(size_t size, size_t align)
459 : : {
460 : 3230 : size_t min_size = (size_t)-1;
461 : 3230 : int min_id = 0;
462 [ + - + + ]: 3230 : for (int i = 0;i < nblocks && blocks[i].ptr;i++) {
463 [ + - ]: 3111 : if (void *ptr = blocks[i].alloc(size, align))
464 : 3111 : return ptr;
465 [ # # ]: 0 : if (blocks[i].avail < min_size) {
466 : 0 : min_size = blocks[i].avail;
467 : 0 : min_id = i;
468 : : }
469 : : }
470 : 119 : size_t block_size = get_block_size(size);
471 : 119 : blocks[min_id].reset(map_anon_page(block_size), block_size);
472 : 119 : return blocks[min_id].alloc(size, align);
473 : : }
474 : : };
475 : :
476 : : struct SplitPtrBlock : public Block {
477 : : // Possible states
478 : : // Allocation:
479 : : // * Initial allocation: `state & InitAlloc`
480 : : // * Followup allocation: `(state & Alloc) && !(state & InitAlloc)`
481 : : enum State {
482 : : // This block has no page protection set yet
483 : : InitAlloc = (1 << 0),
484 : : // There is at least one allocation in this page since last finalization
485 : : Alloc = (1 << 1),
486 : : // `wr_ptr` can be directly used as write address.
487 : : WRInit = (1 << 2),
488 : : // With `WRInit` set, whether `wr_ptr` has write permission enabled.
489 : : WRReady = (1 << 3),
490 : : };
491 : :
492 : : uintptr_t wr_ptr{0};
493 : : uint32_t state{0};
494 : 338 : SplitPtrBlock() = default;
495 : :
496 : 1744 : void swap(SplitPtrBlock &other)
497 : : {
498 : 1744 : std::swap(ptr, other.ptr);
499 : 1744 : std::swap(total, other.total);
500 : 1744 : std::swap(avail, other.avail);
501 : 1744 : std::swap(wr_ptr, other.wr_ptr);
502 : 1744 : std::swap(state, other.state);
503 : 1744 : }
504 : :
505 : 338 : SplitPtrBlock(SplitPtrBlock &&other)
506 : 338 : : SplitPtrBlock()
507 : : {
508 : 338 : swap(other);
509 : 338 : }
510 : : };
511 : :
512 : : struct Allocation {
513 : : // Address to write to (the one returned by the allocation function)
514 : : void *wr_addr;
515 : : // Runtime address
516 : : void *rt_addr;
517 : : size_t sz;
518 : : bool relocated;
519 : : };
520 : :
521 : : template<bool exec>
522 : : class ROAllocator {
523 : : protected:
524 : : static constexpr int nblocks = 8;
525 : : SplitPtrBlock blocks[nblocks];
526 : : // Blocks that are done allocating (removed from `blocks`)
527 : : // but might not have all the permissions set or data copied yet.
528 : : SmallVector<SplitPtrBlock, 16> completed;
529 : : virtual void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr,
530 : : size_t size, size_t align) = 0;
531 : : virtual SplitPtrBlock alloc_block(size_t size) = 0;
532 : : public:
533 : 0 : virtual ~ROAllocator() {}
534 : 668740 : virtual void finalize()
535 : : {
536 [ + + ]: 1453992 : for (auto &alloc: allocations) {
537 : : // ensure the mapped pages are consistent
538 : 785252 : sys::Memory::InvalidateInstructionCache(alloc.wr_addr,
539 : : alloc.sz);
540 : 785252 : sys::Memory::InvalidateInstructionCache(alloc.rt_addr,
541 : : alloc.sz);
542 : : }
543 : 668740 : completed.clear();
544 : 668740 : allocations.clear();
545 : 668740 : }
546 : : // Allocations that have not been finalized yet.
547 : : SmallVector<Allocation, 16> allocations;
548 : 785252 : void *alloc(size_t size, size_t align)
549 : : {
550 : 785252 : size_t min_size = (size_t)-1;
551 : 785252 : int min_id = 0;
552 [ + - + + ]: 785590 : for (int i = 0;i < nblocks && blocks[i].ptr;i++) {
553 : 784184 : auto &block = blocks[i];
554 : 784184 : void *ptr = block.alloc(size, align);
555 [ + + ]: 784184 : if (ptr) {
556 : : void *wr_ptr;
557 [ + + ]: 783846 : if (block.state & SplitPtrBlock::InitAlloc) {
558 : 6 : wr_ptr = ptr;
559 : : }
560 : : else {
561 : 783840 : wr_ptr = get_wr_ptr(block, ptr, size, align);
562 : : }
563 : 783846 : block.state |= SplitPtrBlock::Alloc;
564 : 783846 : allocations.push_back(Allocation{wr_ptr, ptr, size, false});
565 : 783846 : return wr_ptr;
566 : : }
567 [ + - ]: 338 : if (block.avail < min_size) {
568 : 338 : min_size = block.avail;
569 : 338 : min_id = i;
570 : : }
571 : : }
572 : 1406 : size_t block_size = get_block_size(size);
573 : 1406 : auto &block = blocks[min_id];
574 : 1406 : auto new_block = alloc_block(block_size);
575 : 1406 : block.swap(new_block);
576 [ + + ]: 1406 : if (new_block.state) {
577 : 338 : completed.push_back(std::move(new_block));
578 : : }
579 : : else {
580 : 1068 : new_block.reset(nullptr, 0);
581 : : }
582 : 1406 : void *ptr = block.alloc(size, align);
583 : : #ifdef _OS_WINDOWS_
584 : : block.state = SplitPtrBlock::Alloc;
585 : : void *wr_ptr = get_wr_ptr(block, ptr, size, align);
586 : : allocations.push_back(Allocation{wr_ptr, ptr, size, false});
587 : : ptr = wr_ptr;
588 : : #else
589 : 1406 : block.state = SplitPtrBlock::Alloc | SplitPtrBlock::InitAlloc;
590 : 1406 : allocations.push_back(Allocation{ptr, ptr, size, false});
591 : : #endif
592 : 1406 : return ptr;
593 : : }
594 : : };
595 : :
596 : : template<bool exec>
597 : : class DualMapAllocator : public ROAllocator<exec> {
598 : : protected:
599 : 0 : void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr, size_t, size_t) override
600 : : {
601 [ # # # # ]: 0 : assert((char*)rt_ptr >= block.ptr &&
602 : : (char*)rt_ptr < (block.ptr + block.total));
603 [ # # ]: 0 : if (!(block.state & SplitPtrBlock::WRInit)) {
604 : 0 : block.wr_ptr = (uintptr_t)create_shared_map(block.total,
605 : : block.wr_ptr);
606 : 0 : block.state |= SplitPtrBlock::WRInit;
607 : : }
608 [ # # ]: 0 : if (!(block.state & SplitPtrBlock::WRReady)) {
609 : 0 : protect_page((void*)block.wr_ptr, block.total, Prot::RW);
610 : 0 : block.state |= SplitPtrBlock::WRReady;
611 : : }
612 : 0 : return (char*)rt_ptr + (block.wr_ptr - uintptr_t(block.ptr));
613 : : }
614 : 0 : SplitPtrBlock alloc_block(size_t size) override
615 : : {
616 : 0 : SplitPtrBlock new_block;
617 : : // use `wr_ptr` to record the id initially
618 : 0 : auto ptr = alloc_shared_page(size, (size_t*)&new_block.wr_ptr, exec);
619 : 0 : new_block.reset(ptr, size);
620 : 0 : return new_block;
621 : : }
622 : 0 : void finalize_block(SplitPtrBlock &block, bool reset)
623 : : {
624 : : // This function handles setting the block to the right mode
625 : : // and free'ing maps that are not needed anymore.
626 : : // If `reset` is `true`, we won't allocate in this block anymore and
627 : : // we should free up resources that is not needed at runtime.
628 [ # # ]: 0 : if (!(block.state & SplitPtrBlock::Alloc)) {
629 : : // A block that is not used this time, check if we need to free it.
630 [ # # # # ]: 0 : if ((block.state & SplitPtrBlock::WRInit) && reset)
631 : 0 : unmap_page((void*)block.wr_ptr, block.total);
632 : 0 : return;
633 : : }
634 : : // For a block we used this time
635 [ # # ]: 0 : if (block.state & SplitPtrBlock::InitAlloc) {
636 : : // For an initial block, we have a single RW map.
637 : : // Need to map it to RO or RX.
638 [ # # ]: 0 : assert(!(block.state & (SplitPtrBlock::WRReady |
639 : : SplitPtrBlock::WRInit)));
640 : 0 : protect_page(block.ptr, block.total, exec ? Prot::RX : Prot::RO);
641 : 0 : block.state = 0;
642 : : }
643 : : else {
644 : : // For other ones, the runtime address has the correct mode.
645 : : // Need to map the write address to RO.
646 [ # # ]: 0 : assert(block.state & SplitPtrBlock::WRInit);
647 [ # # ]: 0 : assert(block.state & SplitPtrBlock::WRReady);
648 [ # # ]: 0 : if (reset) {
649 : 0 : unmap_page((void*)block.wr_ptr, block.total);
650 : : }
651 : : else {
652 : 0 : protect_page((void*)block.wr_ptr, block.total, Prot::NO);
653 : 0 : block.state = SplitPtrBlock::WRInit;
654 : : }
655 : : }
656 : : }
657 : : public:
658 : 0 : DualMapAllocator()
659 : 0 : {
660 [ # # ]: 0 : assert(anon_hdl != -1);
661 : 0 : }
662 : 0 : void finalize() override
663 : : {
664 [ # # ]: 0 : for (auto &block : this->blocks) {
665 : 0 : finalize_block(block, false);
666 : : }
667 [ # # ]: 0 : for (auto &block : this->completed) {
668 : 0 : finalize_block(block, true);
669 : 0 : block.reset(nullptr, 0);
670 : : }
671 : 0 : ROAllocator<exec>::finalize();
672 : 0 : }
673 : : };
674 : :
675 : : #ifdef _OS_LINUX_
676 : : template<bool exec>
677 : : class SelfMemAllocator : public ROAllocator<exec> {
678 : : SmallVector<Block, 16> temp_buff;
679 : : protected:
680 : 783840 : void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr,
681 : : size_t size, size_t align) override
682 : : {
683 [ - + ]: 783840 : assert(!(block.state & SplitPtrBlock::InitAlloc));
684 [ + + ]: 783840 : for (auto &wr_block: temp_buff) {
685 [ + - ]: 782780 : if (void *ptr = wr_block.alloc(size, align)) {
686 : 782780 : return ptr;
687 : : }
688 : : }
689 : 1060 : temp_buff.emplace_back();
690 : 1060 : Block &new_block = temp_buff.back();
691 : 1060 : size_t block_size = get_block_size(size);
692 : 1060 : new_block.reset(map_anon_page(block_size), block_size);
693 : 1060 : return new_block.alloc(size, align);
694 : : }
695 : 1406 : SplitPtrBlock alloc_block(size_t size) override
696 : : {
697 : 1406 : SplitPtrBlock new_block;
698 : 1406 : new_block.reset(map_anon_page(size), size);
699 : 1406 : return new_block;
700 : : }
701 : 5350260 : void finalize_block(SplitPtrBlock &block, bool reset)
702 : : {
703 [ + + ]: 5350260 : if (!(block.state & SplitPtrBlock::Alloc))
704 : 4681180 : return;
705 [ + + ]: 669078 : if (block.state & SplitPtrBlock::InitAlloc) {
706 : : // for an initial block, we need to map it to ro or rx
707 [ - + ]: 1406 : assert(!(block.state & (SplitPtrBlock::WRReady |
708 : : SplitPtrBlock::WRInit)));
709 : 1406 : protect_page(block.ptr, block.total, exec ? Prot::RX : Prot::RO);
710 : 1406 : block.state = 0;
711 : : }
712 : : }
713 : : public:
714 : 1134 : SelfMemAllocator()
715 : : : ROAllocator<exec>(),
716 : 1134 : temp_buff()
717 : : {
718 [ - + ]: 1134 : assert(get_self_mem_fd() != -1);
719 : 1134 : }
720 : 668740 : void finalize() override
721 : : {
722 [ + + ]: 6018660 : for (auto &block : this->blocks) {
723 : 5349920 : finalize_block(block, false);
724 : : }
725 [ + + ]: 669078 : for (auto &block : this->completed) {
726 : 338 : finalize_block(block, true);
727 : 338 : block.reset(nullptr, 0);
728 : : }
729 [ + + ]: 1453992 : for (auto &alloc : this->allocations) {
730 [ + + ]: 785252 : if (alloc.rt_addr == alloc.wr_addr)
731 : 1412 : continue;
732 : 783840 : write_self_mem(alloc.rt_addr, alloc.wr_addr, alloc.sz);
733 : : }
734 : : // clear all the temp buffers except the first one
735 : : // (we expect only one)
736 : 668740 : bool cached = false;
737 [ + + ]: 1336412 : for (auto &block : temp_buff) {
738 [ - + ]: 667672 : if (cached) {
739 : 0 : munmap(block.ptr, block.total);
740 : 0 : block.ptr = nullptr;
741 : 0 : block.total = block.avail = 0;
742 : : }
743 : : else {
744 : 667672 : block.avail = block.total;
745 : 667672 : cached = true;
746 : : }
747 : : }
748 [ + + ]: 668740 : if (cached)
749 : 667672 : temp_buff.resize(1);
750 : 668740 : ROAllocator<exec>::finalize();
751 : 668740 : }
752 : : };
753 : : #endif // _OS_LINUX_
754 : :
755 : : class RTDyldMemoryManagerJL : public SectionMemoryManager {
756 : : struct EHFrame {
757 : : uint8_t *addr;
758 : : size_t size;
759 : : };
760 : : RTDyldMemoryManagerJL(const RTDyldMemoryManagerJL&) = delete;
761 : : void operator=(const RTDyldMemoryManagerJL&) = delete;
762 : : SmallVector<EHFrame, 16> pending_eh;
763 : : RWAllocator rw_alloc;
764 : : std::unique_ptr<ROAllocator<false>> ro_alloc;
765 : : std::unique_ptr<ROAllocator<true>> exe_alloc;
766 : : bool code_allocated;
767 : : size_t total_allocated;
768 : :
769 : : public:
770 : 567 : RTDyldMemoryManagerJL()
771 : 567 : : SectionMemoryManager(),
772 : : pending_eh(),
773 : : rw_alloc(),
774 : : ro_alloc(),
775 : : exe_alloc(),
776 : : code_allocated(false),
777 : 567 : total_allocated(0)
778 : : {
779 : : #ifdef _OS_LINUX_
780 [ + - + - : 567 : if (!ro_alloc && get_self_mem_fd() != -1) {
+ - ]
781 : 567 : ro_alloc.reset(new SelfMemAllocator<false>());
782 : 567 : exe_alloc.reset(new SelfMemAllocator<true>());
783 : : }
784 : : #endif
785 [ - + - - : 567 : if (!ro_alloc && init_shared_map() != -1) {
- + ]
786 : 0 : ro_alloc.reset(new DualMapAllocator<false>());
787 : 0 : exe_alloc.reset(new DualMapAllocator<true>());
788 : : }
789 : 567 : }
790 : 0 : ~RTDyldMemoryManagerJL() override
791 : 0 : {
792 : 0 : }
793 : 1 : size_t getTotalBytes() { return total_allocated; }
794 : : void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
795 : : size_t Size) override;
796 : : #if 0
797 : : // Disable for now since we are not actually using this.
798 : : void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr,
799 : : size_t Size) override;
800 : : #endif
801 : : uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
802 : : unsigned SectionID,
803 : : StringRef SectionName) override;
804 : : uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
805 : : unsigned SectionID, StringRef SectionName,
806 : : bool isReadOnly) override;
807 : : using SectionMemoryManager::notifyObjectLoaded;
808 : : void notifyObjectLoaded(RuntimeDyld &Dyld,
809 : : const object::ObjectFile &Obj) override;
810 : : bool finalizeMemory(std::string *ErrMsg = nullptr) override;
811 : : template <typename DL, typename Alloc>
812 : 668740 : void mapAddresses(DL &Dyld, Alloc &&allocator)
813 : : {
814 [ + + ]: 1453992 : for (auto &alloc: allocator->allocations) {
815 [ + + - + ]: 785252 : if (alloc.rt_addr == alloc.wr_addr || alloc.relocated)
816 : 1412 : continue;
817 : 783840 : alloc.relocated = true;
818 : 783840 : Dyld.mapSectionAddress(alloc.wr_addr, (uintptr_t)alloc.rt_addr);
819 : : }
820 : 668740 : }
821 : : template <typename DL>
822 : 334370 : void mapAddresses(DL &Dyld)
823 : : {
824 [ - + ]: 334370 : if (!ro_alloc)
825 : 0 : return;
826 : 334370 : mapAddresses(Dyld, ro_alloc);
827 : 334370 : mapAddresses(Dyld, exe_alloc);
828 : : }
829 : : #ifdef _OS_WINDOWS_
830 : : template <typename Alloc>
831 : : void *lookupWriteAddressFor(void *rt_addr, Alloc &&allocator)
832 : : {
833 : : for (auto &alloc: allocator->allocations) {
834 : : if (alloc.rt_addr == rt_addr) {
835 : : return alloc.wr_addr;
836 : : }
837 : : }
838 : : return nullptr;
839 : : }
840 : : void *lookupWriteAddressFor(void *rt_addr)
841 : : {
842 : : if (!ro_alloc)
843 : : return rt_addr;
844 : : if (void *ptr = lookupWriteAddressFor(rt_addr, ro_alloc))
845 : : return ptr;
846 : : if (void *ptr = lookupWriteAddressFor(rt_addr, exe_alloc))
847 : : return ptr;
848 : : return rt_addr;
849 : : }
850 : : #endif // _OS_WINDOWS_
851 : : };
852 : :
853 : 334347 : uint8_t *RTDyldMemoryManagerJL::allocateCodeSection(uintptr_t Size,
854 : : unsigned Alignment,
855 : : unsigned SectionID,
856 : : StringRef SectionName)
857 : : {
858 : : // allocating more than one code section can confuse libunwind.
859 [ - + ]: 334347 : assert(!code_allocated);
860 : 334347 : code_allocated = true;
861 : 334347 : total_allocated += Size;
862 [ + - ]: 334347 : if (exe_alloc)
863 : 334347 : return (uint8_t*)exe_alloc->alloc(Size, Alignment);
864 : 0 : return SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID,
865 : 0 : SectionName);
866 : : }
867 : :
868 : 454135 : uint8_t *RTDyldMemoryManagerJL::allocateDataSection(uintptr_t Size,
869 : : unsigned Alignment,
870 : : unsigned SectionID,
871 : : StringRef SectionName,
872 : : bool isReadOnly)
873 : : {
874 : 454135 : total_allocated += Size;
875 [ + + ]: 454135 : if (!isReadOnly)
876 : 3230 : return (uint8_t*)rw_alloc.alloc(Size, Alignment);
877 [ + - ]: 450905 : if (ro_alloc)
878 : 450905 : return (uint8_t*)ro_alloc->alloc(Size, Alignment);
879 : 0 : return SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID,
880 : 0 : SectionName, isReadOnly);
881 : : }
882 : :
883 : 334370 : void RTDyldMemoryManagerJL::notifyObjectLoaded(RuntimeDyld &Dyld,
884 : : const object::ObjectFile &Obj)
885 : : {
886 [ - + ]: 334370 : if (!ro_alloc) {
887 [ # # ]: 0 : assert(!exe_alloc);
888 : 0 : SectionMemoryManager::notifyObjectLoaded(Dyld, Obj);
889 : 0 : return;
890 : : }
891 [ - + ]: 334370 : assert(exe_alloc);
892 : 334370 : mapAddresses(Dyld);
893 : : }
894 : :
895 : 334370 : bool RTDyldMemoryManagerJL::finalizeMemory(std::string *ErrMsg)
896 : : {
897 : 334370 : code_allocated = false;
898 [ + - ]: 334370 : if (ro_alloc) {
899 : 334370 : ro_alloc->finalize();
900 [ - + ]: 334370 : assert(exe_alloc);
901 : 334370 : exe_alloc->finalize();
902 [ + + ]: 668154 : for (auto &frame: pending_eh)
903 : 333784 : register_eh_frames(frame.addr, frame.size);
904 : 334370 : pending_eh.clear();
905 : 334370 : return false;
906 : : }
907 : : else {
908 [ # # ]: 0 : assert(!exe_alloc);
909 : 0 : return SectionMemoryManager::finalizeMemory(ErrMsg);
910 : : }
911 : : }
912 : :
913 : 334347 : void RTDyldMemoryManagerJL::registerEHFrames(uint8_t *Addr,
914 : : uint64_t LoadAddr,
915 : : size_t Size)
916 : : {
917 [ + + ]: 334347 : if (uintptr_t(Addr) == LoadAddr) {
918 : 563 : register_eh_frames(Addr, Size);
919 : : }
920 : : else {
921 : 333784 : pending_eh.push_back(EHFrame{(uint8_t*)(uintptr_t)LoadAddr, Size});
922 : : }
923 : 334347 : }
924 : :
925 : : #if 0
926 : : void RTDyldMemoryManagerJL::deregisterEHFrames(uint8_t *Addr,
927 : : uint64_t LoadAddr,
928 : : size_t Size)
929 : : {
930 : : deregister_eh_frames((uint8_t*)LoadAddr, Size);
931 : : }
932 : : #endif
933 : :
934 : : }
935 : :
936 : : #ifdef _OS_WINDOWS_
937 : : void *lookupWriteAddressFor(RTDyldMemoryManager *memmgr, void *rt_addr)
938 : : {
939 : : return ((RTDyldMemoryManagerJL*)memmgr)->lookupWriteAddressFor(rt_addr);
940 : : }
941 : : #endif
942 : :
943 : 567 : RTDyldMemoryManager* createRTDyldMemoryManager()
944 : : {
945 : 567 : return new RTDyldMemoryManagerJL();
946 : : }
947 : :
948 : 1 : size_t getRTDyldMemoryManagerTotalBytes(RTDyldMemoryManager *mm)
949 : : {
950 : 1 : return ((RTDyldMemoryManagerJL*)mm)->getTotalBytes();
951 : : }
|