1 /* -----------------------------------------------------------------------
2 closures.c - Copyright (c) 2019 Anthony Green
3 Copyright (c) 2007, 2009, 2010 Red Hat, Inc.
4 Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
5 Copyright (c) 2011 Plausible Labs Cooperative, Inc.
7 Code to allocate and deallocate memory for closures.
9 Permission is hereby granted, free of charge, to any person obtaining
10 a copy of this software and associated documentation files (the
11 ``Software''), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
17 The above copyright notice and this permission notice shall be included
18 in all copies or substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 DEALINGS IN THE SOFTWARE.
28 ----------------------------------------------------------------------- */
30 #if defined __linux__ && !defined _GNU_SOURCE
34 #include <fficonfig.h>
36 #include <ffi_common.h>
40 #include <sys/param.h>
43 #if __NetBSD_Version__ - 0 >= 799007200
44 /* NetBSD with PROT_MPROTECT */
49 #ifdef HAVE_SYS_MEMFD_H
50 #include <sys/memfd.h>
53 static const size_t overhead =
54 (sizeof(max_align_t) > sizeof(void *) + sizeof(size_t)) ?
56 : sizeof(void *) + sizeof(size_t);
58 #define ADD_TO_POINTER(p, d) ((void *)((uintptr_t)(p) + (d)))
61 ffi_closure_alloc (size_t size, void **code)
63 static size_t page_size;
65 void *codeseg, *dataseg;
68 /* Expect that PAX mprotect is active and a separate code mapping is necessary. */
72 /* Obtain system page size. */
74 page_size = sysconf(_SC_PAGESIZE);
76 /* Round allocation size up to the next page, keeping in mind the size field and pointer to code map. */
77 rounded_size = (size + overhead + page_size - 1) & ~(page_size - 1);
79 /* Primary mapping is RW, but request permission to switch to PROT_EXEC later. */
80 prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
81 dataseg = mmap(NULL, rounded_size, prot, MAP_ANON | MAP_PRIVATE, -1, 0);
82 if (dataseg == MAP_FAILED)
85 /* Create secondary mapping and switch it to RX. */
86 codeseg = mremap(dataseg, rounded_size, NULL, rounded_size, MAP_REMAPDUP);
87 if (codeseg == MAP_FAILED) {
88 munmap(dataseg, rounded_size);
91 if (mprotect(codeseg, rounded_size, PROT_READ | PROT_EXEC) == -1) {
92 munmap(codeseg, rounded_size);
93 munmap(dataseg, rounded_size);
97 /* Remember allocation size and location of the secondary mapping for ffi_closure_free. */
98 memcpy(dataseg, &rounded_size, sizeof(rounded_size));
99 memcpy(ADD_TO_POINTER(dataseg, sizeof(size_t)), &codeseg, sizeof(void *));
100 *code = ADD_TO_POINTER(codeseg, overhead);
101 return ADD_TO_POINTER(dataseg, overhead);
105 ffi_closure_free (void *ptr)
107 void *codeseg, *dataseg;
110 dataseg = ADD_TO_POINTER(ptr, -overhead);
111 memcpy(&rounded_size, dataseg, sizeof(rounded_size));
112 memcpy(&codeseg, ADD_TO_POINTER(dataseg, sizeof(size_t)), sizeof(void *));
113 munmap(dataseg, rounded_size);
114 munmap(codeseg, rounded_size);
118 ffi_tramp_is_present (__attribute__((unused)) void *ptr)
122 #else /* !NetBSD with PROT_MPROTECT */
124 #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
125 # if __linux__ && !defined(__ANDROID__)
126 /* This macro indicates it may be forbidden to map anonymous memory
127 with both write and execute permission. Code compiled when this
128 option is defined will attempt to map such pages once, but if it
129 fails, it falls back to creating a temporary file in a writable and
130 executable filesystem and mapping pages from it into separate
131 locations in the virtual memory space, one location writable and
132 another executable. */
133 # define FFI_MMAP_EXEC_WRIT 1
134 # define HAVE_MNTENT 1
136 # if defined(_WIN32) || defined(__OS2__)
137 /* Windows systems may have Data Execution Protection (DEP) enabled,
138 which requires the use of VirtualMalloc/VirtualFree to alloc/free
139 executable memory. */
140 # define FFI_MMAP_EXEC_WRIT 1
144 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
145 # if defined(__linux__) && !defined(__ANDROID__)
146 /* When defined to 1 check for SELinux and if SELinux is active,
147 don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
148 might cause audit messages. */
149 # define FFI_MMAP_EXEC_SELINUX 1
155 #if FFI_EXEC_TRAMPOLINE_TABLE
159 #include <mach/mach.h>
167 extern void *ffi_closure_trampoline_table_page;
169 typedef struct ffi_trampoline_table ffi_trampoline_table;
170 typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
172 struct ffi_trampoline_table
174 /* contiguous writable and executable pages */
175 vm_address_t config_page;
176 vm_address_t trampoline_page;
178 /* free list tracking */
180 ffi_trampoline_table_entry *free_list;
181 ffi_trampoline_table_entry *free_list_pool;
183 ffi_trampoline_table *prev;
184 ffi_trampoline_table *next;
187 struct ffi_trampoline_table_entry
189 void *(*trampoline) (void);
190 ffi_trampoline_table_entry *next;
193 /* Total number of trampolines that fit in one trampoline table */
194 #define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
196 static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
197 static ffi_trampoline_table *ffi_trampoline_tables = NULL;
199 static ffi_trampoline_table *
200 ffi_trampoline_table_alloc (void)
202 ffi_trampoline_table *table;
203 vm_address_t config_page;
204 vm_address_t trampoline_page;
205 vm_address_t trampoline_page_template;
211 /* Allocate two pages -- a config page and a placeholder page */
213 kt = vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
215 if (kt != KERN_SUCCESS)
218 /* Remap the trampoline table on top of the placeholder page */
219 trampoline_page = config_page + PAGE_MAX_SIZE;
220 trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
222 /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
223 trampoline_page_template &= ~1UL;
225 kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0,
226 VM_FLAGS_OVERWRITE, mach_task_self (), trampoline_page_template,
227 FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
228 if (kt != KERN_SUCCESS)
230 vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
234 /* We have valid trampoline and config pages */
235 table = calloc (1, sizeof (ffi_trampoline_table));
236 table->free_count = FFI_TRAMPOLINE_COUNT;
237 table->config_page = config_page;
238 table->trampoline_page = trampoline_page;
240 /* Create and initialize the free list */
241 table->free_list_pool =
242 calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
244 for (i = 0; i < table->free_count; i++)
246 ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
248 (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
250 if (i < table->free_count - 1)
251 entry->next = &table->free_list_pool[i + 1];
254 table->free_list = table->free_list_pool;
260 ffi_trampoline_table_free (ffi_trampoline_table *table)
262 /* Remove from the list */
263 if (table->prev != NULL)
264 table->prev->next = table->next;
266 if (table->next != NULL)
267 table->next->prev = table->prev;
269 /* Deallocate pages */
270 vm_deallocate (mach_task_self (), table->config_page, PAGE_MAX_SIZE * 2);
272 /* Deallocate free list */
273 free (table->free_list_pool);
278 ffi_closure_alloc (size_t size, void **code)
280 /* Create the closure */
281 ffi_closure *closure = malloc (size);
285 pthread_mutex_lock (&ffi_trampoline_lock);
287 /* Check for an active trampoline table with available entries. */
288 ffi_trampoline_table *table = ffi_trampoline_tables;
289 if (table == NULL || table->free_list == NULL)
291 table = ffi_trampoline_table_alloc ();
294 pthread_mutex_unlock (&ffi_trampoline_lock);
299 /* Insert the new table at the top of the list */
300 table->next = ffi_trampoline_tables;
301 if (table->next != NULL)
302 table->next->prev = table;
304 ffi_trampoline_tables = table;
307 /* Claim the free entry */
308 ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
309 ffi_trampoline_tables->free_list = entry->next;
310 ffi_trampoline_tables->free_count--;
313 pthread_mutex_unlock (&ffi_trampoline_lock);
315 /* Initialize the return values */
316 *code = entry->trampoline;
318 *code = ptrauth_sign_unauthenticated (*code, ptrauth_key_asia, 0);
320 closure->trampoline_table = table;
321 closure->trampoline_table_entry = entry;
327 ffi_closure_free (void *ptr)
329 ffi_closure *closure = ptr;
331 pthread_mutex_lock (&ffi_trampoline_lock);
333 /* Fetch the table and entry references */
334 ffi_trampoline_table *table = closure->trampoline_table;
335 ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
337 /* Return the entry to the free list */
338 entry->next = table->free_list;
339 table->free_list = entry;
342 /* If all trampolines within this table are free, and at least one other table exists, deallocate
344 if (table->free_count == FFI_TRAMPOLINE_COUNT
345 && ffi_trampoline_tables != table)
347 ffi_trampoline_table_free (table);
349 else if (ffi_trampoline_tables != table)
351 /* Otherwise, bump this table to the top of the list */
353 table->next = ffi_trampoline_tables;
354 if (ffi_trampoline_tables != NULL)
355 ffi_trampoline_tables->prev = table;
357 ffi_trampoline_tables = table;
360 pthread_mutex_unlock (&ffi_trampoline_lock);
362 /* Free the closure */
368 // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
370 #elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
373 #define USE_DL_PREFIX 1
375 #ifndef USE_BUILTIN_FFS
376 #define USE_BUILTIN_FFS 1
380 /* We need to use mmap, not sbrk. */
381 #define HAVE_MORECORE 0
383 /* We could, in theory, support mremap, but it wouldn't buy us anything. */
384 #define HAVE_MREMAP 0
386 /* We have no use for this, so save some code and data. */
387 #define NO_MALLINFO 1
389 /* We need all allocations to be in regular segments, otherwise we
390 lose track of the corresponding code address. */
391 #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
393 /* Don't allocate more than a page unless needed. */
394 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
396 #include <sys/types.h>
397 #include <sys/stat.h>
408 #endif /* HAVE_MNTENT */
409 #include <sys/param.h>
412 /* We don't want sys/mman.h to be included after we redefine mmap and
414 #include <sys/mman.h>
415 #define LACKS_SYS_MMAN_H 1
417 #if FFI_MMAP_EXEC_SELINUX
418 #include <sys/statfs.h>
421 static int selinux_enabled = -1;
424 selinux_enabled_check (void)
431 if (statfs ("/selinux", &sfs) >= 0
432 && (unsigned int) sfs.f_type == 0xf97cff8cU)
434 f = fopen ("/proc/mounts", "r");
437 while (getline (&buf, &len, f) >= 0)
439 char *p = strchr (buf, ' ');
442 p = strchr (p + 1, ' ');
445 if (strncmp (p + 1, "selinuxfs ", 10) == 0)
457 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
458 : (selinux_enabled = selinux_enabled_check ()))
462 #define is_selinux_enabled() 0
464 #endif /* !FFI_MMAP_EXEC_SELINUX */
466 /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
467 #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
470 static int emutramp_enabled = -1;
473 emutramp_enabled_check (void)
479 f = fopen ("/proc/self/status", "r");
484 while (getline (&buf, &len, f) != -1)
485 if (!strncmp (buf, "PaX:", 4))
488 if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
489 ret = (emutramp == 'E');
497 #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
498 : (emutramp_enabled = emutramp_enabled_check ()))
499 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
501 #elif defined (__CYGWIN__) || defined(__INTERIX)
503 #include <sys/mman.h>
505 /* Cygwin is Linux-like, but not quite that Linux-like. */
506 #define is_selinux_enabled() 0
508 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
510 #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
511 #define is_emutramp_enabled() 0
512 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
514 /* Declare all functions defined in dlmalloc.c as static. */
515 static void *dlmalloc(size_t);
516 static void dlfree(void*);
517 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
518 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
519 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
520 static void *dlvalloc(size_t) MAYBE_UNUSED;
521 static int dlmallopt(int, int) MAYBE_UNUSED;
522 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
523 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
524 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
525 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
526 static void *dlpvalloc(size_t) MAYBE_UNUSED;
527 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
528 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
529 static void dlmalloc_stats(void) MAYBE_UNUSED;
531 #if !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
532 /* Use these for mmap and munmap within dlmalloc.c. */
533 static void *dlmmap(void *, size_t, int, int, int, off_t);
534 static int dlmunmap(void *, size_t);
535 #endif /* !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
538 #define munmap dlmunmap
540 #include "dlmalloc.c"
545 #if !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
547 /* A mutex used to synchronize access to *exec* variables in this file. */
548 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
550 /* A file descriptor of a temporary file from which we'll map
552 static int execfd = -1;
554 /* The amount of space already allocated from the temporary file. */
555 static size_t execsize = 0;
557 #ifdef HAVE_MEMFD_CREATE
558 /* Open a temporary file name, and immediately unlink it. */
560 open_temp_exec_file_memfd (const char *name)
563 fd = memfd_create (name, MFD_CLOEXEC);
568 /* Open a temporary file name, and immediately unlink it. */
570 open_temp_exec_file_name (char *name, int flags)
575 fd = mkostemp (name, flags);
586 /* Open a temporary file in the named directory. */
588 open_temp_exec_file_dir (const char *dir)
590 static const char suffix[] = "/ffiXXXXXX";
604 fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
605 /* If the running system does not support the O_TMPFILE flag then retry without it. */
606 if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
613 lendir = (int) strlen (dir);
614 tempname = __builtin_alloca (lendir + sizeof (suffix));
619 memcpy (tempname, dir, lendir);
620 memcpy (tempname + lendir, suffix, sizeof (suffix));
622 return open_temp_exec_file_name (tempname, flags);
625 /* Open a temporary file in the directory in the named environment
628 open_temp_exec_file_env (const char *envvar)
630 const char *value = getenv (envvar);
635 return open_temp_exec_file_dir (value);
639 /* Open a temporary file in an executable and writable mount point
640 listed in the mounts file. Subsequent calls with the same mounts
641 keep searching for mount points in the same file. Providing NULL
642 as the mounts file closes the file. */
644 open_temp_exec_file_mnt (const char *mounts)
646 static const char *last_mounts;
647 static FILE *last_mntent;
649 if (mounts != last_mounts)
652 endmntent (last_mntent);
654 last_mounts = mounts;
657 last_mntent = setmntent (mounts, "r");
669 char buf[MAXPATHLEN * 3];
671 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
674 if (hasmntopt (&mnt, "ro")
675 || hasmntopt (&mnt, "noexec")
676 || access (mnt.mnt_dir, W_OK))
679 fd = open_temp_exec_file_dir (mnt.mnt_dir);
685 #endif /* HAVE_MNTENT */
687 /* Instructions to look for a location to hold a temporary file that
688 can be mapped in for execution. */
691 int (*func)(const char *);
694 } open_temp_exec_file_opts[] = {
695 #ifdef HAVE_MEMFD_CREATE
696 { open_temp_exec_file_memfd, "libffi", 0 },
698 { open_temp_exec_file_env, "TMPDIR", 0 },
699 { open_temp_exec_file_dir, "/tmp", 0 },
700 { open_temp_exec_file_dir, "/var/tmp", 0 },
701 { open_temp_exec_file_dir, "/dev/shm", 0 },
702 { open_temp_exec_file_env, "HOME", 0 },
704 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
705 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
706 #endif /* HAVE_MNTENT */
709 /* Current index into open_temp_exec_file_opts. */
710 static int open_temp_exec_file_opts_idx = 0;
712 /* Reset a current multi-call func, then advances to the next entry.
713 If we're at the last, go back to the first and return nonzero,
714 otherwise return zero. */
716 open_temp_exec_file_opts_next (void)
718 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
719 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
721 open_temp_exec_file_opts_idx++;
722 if (open_temp_exec_file_opts_idx
723 == (sizeof (open_temp_exec_file_opts)
724 / sizeof (*open_temp_exec_file_opts)))
726 open_temp_exec_file_opts_idx = 0;
733 /* Return a file descriptor of a temporary zero-sized file in a
734 writable and executable filesystem. */
736 open_temp_exec_file (void)
742 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
743 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
745 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
748 if (open_temp_exec_file_opts_next ())
757 /* We need to allocate space in a file that will be backing a writable
758 mapping. Several problems exist with the usual approaches:
759 - fallocate() is Linux-only
760 - posix_fallocate() is not available on all platforms
761 - ftruncate() does not allocate space on filesystems with sparse files
762 Failure to allocate the space will cause SIGBUS to be thrown when
763 the mapping is subsequently written to. */
765 allocate_space (int fd, off_t offset, off_t len)
767 static size_t page_size;
769 /* Obtain system page size. */
771 page_size = sysconf(_SC_PAGESIZE);
773 unsigned char buf[page_size];
774 memset (buf, 0, page_size);
778 off_t to_write = (len < page_size) ? len : page_size;
779 if (write (fd, buf, to_write) < to_write)
787 /* Map in a chunk of memory from the temporary exec file into separate
788 locations in the virtual memory address space, one writable and one
789 executable. Returns the address of the writable portion, after
790 storing an offset to the corresponding executable portion at the
791 last word of the requested chunk. */
793 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
799 open_temp_exec_file_opts_idx = 0;
801 execfd = open_temp_exec_file ();
808 if (allocate_space (execfd, offset, length))
811 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
814 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
815 flags, execfd, offset);
823 if (ftruncate (execfd, offset) != 0)
825 /* Fixme : Error logs can be added here. Returning an error for
826 * ftruncte() will not add any advantage as it is being
827 * validating in the error case. */
833 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
834 open_temp_exec_file_opts_next ();
836 start = mmap (start, length, prot, flags, execfd, offset);
840 munmap (ptr, length);
841 if (ftruncate (execfd, offset) != 0)
843 /* Fixme : Error logs can be added here. Returning an error for
844 * ftruncte() will not add any advantage as it is being
845 * validating in the error case. */
850 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
857 /* Map in a writable and executable chunk of memory if possible.
858 Failing that, fall back to dlmmap_locked. */
860 dlmmap (void *start, size_t length, int prot,
861 int flags, int fd, off_t offset)
865 assert (start == NULL && length % malloc_getpagesize == 0
866 && prot == (PROT_READ | PROT_WRITE)
867 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
868 && fd == -1 && offset == 0);
870 if (execfd == -1 && ffi_tramp_is_supported ())
872 ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
876 if (execfd == -1 && is_emutramp_enabled ())
878 ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
882 if (execfd == -1 && !is_selinux_enabled ())
884 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
886 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
887 /* Cool, no need to mess with separate segments. */
890 /* If MREMAP_DUP is ever introduced and implemented, try mmap
891 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
892 MREMAP_DUP and prot at this point. */
895 if (execsize == 0 || execfd == -1)
897 pthread_mutex_lock (&open_temp_exec_file_mutex);
898 ptr = dlmmap_locked (start, length, prot, flags, offset);
899 pthread_mutex_unlock (&open_temp_exec_file_mutex);
904 return dlmmap_locked (start, length, prot, flags, offset);
907 /* Release memory at the given address, as well as the corresponding
908 executable page if it's separate. */
910 dlmunmap (void *start, size_t length)
912 /* We don't bother decreasing execsize or truncating the file, since
913 we can't quite tell whether we're unmapping the end of the file.
914 We don't expect frequent deallocation anyway. If we did, we
915 could locate pages in the file by writing to the pages being
916 deallocated and checking that the file contents change.
918 msegmentptr seg = segment_holding (gm, start);
921 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
923 int ret = munmap (code, length);
928 return munmap (start, length);
931 #if FFI_CLOSURE_FREE_CODE
932 /* Return segment holding given code address. */
934 segment_holding_code (mstate m, char* addr)
936 msegmentptr sp = &m->seg;
938 if (addr >= add_segment_exec_offset (sp->base, sp)
939 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
941 if ((sp = sp->next) == 0)
947 #endif /* !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
949 /* Allocate a chunk of memory with the given size. Returns a pointer
950 to the writable address, and sets *CODE to the executable
951 corresponding virtual address. */
953 ffi_closure_alloc (size_t size, void **code)
960 ptr = FFI_CLOSURE_PTR (dlmalloc (size));
964 msegmentptr seg = segment_holding (gm, ptr);
966 *code = add_segment_exec_offset (ptr, seg);
967 if (!ffi_tramp_is_supported ())
970 ftramp = ffi_tramp_alloc (0);
973 dlfree (FFI_RESTORE_PTR (ptr));
976 *code = ffi_tramp_get_addr (ftramp);
977 ((ffi_closure *) ptr)->ftramp = ftramp;
984 ffi_data_to_code_pointer (void *data)
986 msegmentptr seg = segment_holding (gm, data);
987 /* We expect closures to be allocated with ffi_closure_alloc(), in
988 which case seg will be non-NULL. However, some users take on the
989 burden of managing this memory themselves, in which case this
990 we'll just return data. */
993 if (!ffi_tramp_is_supported ())
994 return add_segment_exec_offset (data, seg);
995 return ffi_tramp_get_addr (((ffi_closure *) data)->ftramp);
1001 /* Release a chunk of memory allocated with ffi_closure_alloc. If
1002 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
1003 writable or the executable address given. Otherwise, only the
1004 writable address can be provided here. */
1006 ffi_closure_free (void *ptr)
1008 #if FFI_CLOSURE_FREE_CODE
1009 msegmentptr seg = segment_holding_code (gm, ptr);
1012 ptr = sub_segment_exec_offset (ptr, seg);
1014 if (ffi_tramp_is_supported ())
1015 ffi_tramp_free (((ffi_closure *) ptr)->ftramp);
1017 dlfree (FFI_RESTORE_PTR (ptr));
1021 ffi_tramp_is_present (void *ptr)
1023 msegmentptr seg = segment_holding (gm, ptr);
1024 return seg != NULL && ffi_tramp_is_supported();
1027 # else /* ! FFI_MMAP_EXEC_WRIT */
1029 /* On many systems, memory returned by malloc is writable and
1030 executable, so just use it. */
1035 ffi_closure_alloc (size_t size, void **code)
1040 return *code = FFI_CLOSURE_PTR (malloc (size));
1044 ffi_closure_free (void *ptr)
1046 free (FFI_RESTORE_PTR (ptr));
1050 ffi_data_to_code_pointer (void *data)
1056 ffi_tramp_is_present (__attribute__((unused)) void *ptr)
1061 # endif /* ! FFI_MMAP_EXEC_WRIT */
1062 #endif /* FFI_CLOSURES */
1064 #endif /* NetBSD with PROT_MPROTECT */