Static tramp v5 (#624)
[platform/upstream/libffi.git] / src / closures.c
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.
6
7    Code to allocate and deallocate memory for closures.
8
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:
16
17    The above copyright notice and this permission notice shall be included
18    in all copies or substantial portions of the Software.
19
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    ----------------------------------------------------------------------- */
29
30 #if defined __linux__ && !defined _GNU_SOURCE
31 #define _GNU_SOURCE 1
32 #endif
33
34 #include <fficonfig.h>
35 #include <ffi.h>
36 #include <ffi_common.h>
37 #include <tramp.h>
38
39 #ifdef __NetBSD__
40 #include <sys/param.h>
41 #endif
42
43 #if __NetBSD_Version__ - 0 >= 799007200
44 /* NetBSD with PROT_MPROTECT */
45 #include <sys/mman.h>
46
47 #include <stddef.h>
48 #include <unistd.h>
49 #ifdef  HAVE_SYS_MEMFD_H
50 #include <sys/memfd.h>
51 #endif
52
53 static const size_t overhead =
54   (sizeof(max_align_t) > sizeof(void *) + sizeof(size_t)) ?
55     sizeof(max_align_t)
56     : sizeof(void *) + sizeof(size_t);
57
58 #define ADD_TO_POINTER(p, d) ((void *)((uintptr_t)(p) + (d)))
59
60 void *
61 ffi_closure_alloc (size_t size, void **code)
62 {
63   static size_t page_size;
64   size_t rounded_size;
65   void *codeseg, *dataseg;
66   int prot;
67
68   /* Expect that PAX mprotect is active and a separate code mapping is necessary. */
69   if (!code)
70     return NULL;
71
72   /* Obtain system page size. */
73   if (!page_size)
74     page_size = sysconf(_SC_PAGESIZE);
75
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);
78
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)
83     return NULL;
84
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);
89     return NULL;
90   }
91   if (mprotect(codeseg, rounded_size, PROT_READ | PROT_EXEC) == -1) {
92     munmap(codeseg, rounded_size);
93     munmap(dataseg, rounded_size);
94     return NULL;
95   }
96
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);
102 }
103
104 void
105 ffi_closure_free (void *ptr)
106 {
107   void *codeseg, *dataseg;
108   size_t rounded_size;
109
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);
115 }
116
117 int
118 ffi_tramp_is_present (__attribute__((unused)) void *ptr)
119 {
120   return 0;
121 }
122 #else /* !NetBSD with PROT_MPROTECT */
123
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
135 # endif
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
141 # endif
142 #endif
143
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
150 # endif
151 #endif
152
153 #if FFI_CLOSURES
154
155 #if FFI_EXEC_TRAMPOLINE_TABLE
156
157 #ifdef __MACH__
158
159 #include <mach/mach.h>
160 #include <pthread.h>
161 #ifdef HAVE_PTRAUTH
162 #include <ptrauth.h>
163 #endif
164 #include <stdio.h>
165 #include <stdlib.h>
166
167 extern void *ffi_closure_trampoline_table_page;
168
169 typedef struct ffi_trampoline_table ffi_trampoline_table;
170 typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
171
172 struct ffi_trampoline_table
173 {
174   /* contiguous writable and executable pages */
175   vm_address_t config_page;
176   vm_address_t trampoline_page;
177
178   /* free list tracking */
179   uint16_t free_count;
180   ffi_trampoline_table_entry *free_list;
181   ffi_trampoline_table_entry *free_list_pool;
182
183   ffi_trampoline_table *prev;
184   ffi_trampoline_table *next;
185 };
186
187 struct ffi_trampoline_table_entry
188 {
189   void *(*trampoline) (void);
190   ffi_trampoline_table_entry *next;
191 };
192
193 /* Total number of trampolines that fit in one trampoline table */
194 #define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
195
196 static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
197 static ffi_trampoline_table *ffi_trampoline_tables = NULL;
198
199 static ffi_trampoline_table *
200 ffi_trampoline_table_alloc (void)
201 {
202   ffi_trampoline_table *table;
203   vm_address_t config_page;
204   vm_address_t trampoline_page;
205   vm_address_t trampoline_page_template;
206   vm_prot_t cur_prot;
207   vm_prot_t max_prot;
208   kern_return_t kt;
209   uint16_t i;
210
211   /* Allocate two pages -- a config page and a placeholder page */
212   config_page = 0x0;
213   kt = vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
214                     VM_FLAGS_ANYWHERE);
215   if (kt != KERN_SUCCESS)
216     return NULL;
217
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;
221 #ifdef __arm__
222   /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
223   trampoline_page_template &= ~1UL;
224 #endif
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)
229     {
230       vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
231       return NULL;
232     }
233
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;
239
240   /* Create and initialize the free list */
241   table->free_list_pool =
242     calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
243
244   for (i = 0; i < table->free_count; i++)
245     {
246       ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
247       entry->trampoline =
248         (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
249
250       if (i < table->free_count - 1)
251         entry->next = &table->free_list_pool[i + 1];
252     }
253
254   table->free_list = table->free_list_pool;
255
256   return table;
257 }
258
259 static void
260 ffi_trampoline_table_free (ffi_trampoline_table *table)
261 {
262   /* Remove from the list */
263   if (table->prev != NULL)
264     table->prev->next = table->next;
265
266   if (table->next != NULL)
267     table->next->prev = table->prev;
268
269   /* Deallocate pages */
270   vm_deallocate (mach_task_self (), table->config_page, PAGE_MAX_SIZE * 2);
271
272   /* Deallocate free list */
273   free (table->free_list_pool);
274   free (table);
275 }
276
277 void *
278 ffi_closure_alloc (size_t size, void **code)
279 {
280   /* Create the closure */
281   ffi_closure *closure = malloc (size);
282   if (closure == NULL)
283     return NULL;
284
285   pthread_mutex_lock (&ffi_trampoline_lock);
286
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)
290     {
291       table = ffi_trampoline_table_alloc ();
292       if (table == NULL)
293         {
294           pthread_mutex_unlock (&ffi_trampoline_lock);
295           free (closure);
296           return NULL;
297         }
298
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;
303
304       ffi_trampoline_tables = table;
305     }
306
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--;
311   entry->next = NULL;
312
313   pthread_mutex_unlock (&ffi_trampoline_lock);
314
315   /* Initialize the return values */
316   *code = entry->trampoline;
317 #ifdef HAVE_PTRAUTH
318   *code = ptrauth_sign_unauthenticated (*code, ptrauth_key_asia, 0);
319 #endif
320   closure->trampoline_table = table;
321   closure->trampoline_table_entry = entry;
322
323   return closure;
324 }
325
326 void
327 ffi_closure_free (void *ptr)
328 {
329   ffi_closure *closure = ptr;
330
331   pthread_mutex_lock (&ffi_trampoline_lock);
332
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;
336
337   /* Return the entry to the free list */
338   entry->next = table->free_list;
339   table->free_list = entry;
340   table->free_count++;
341
342   /* If all trampolines within this table are free, and at least one other table exists, deallocate
343    * the table */
344   if (table->free_count == FFI_TRAMPOLINE_COUNT
345       && ffi_trampoline_tables != table)
346     {
347       ffi_trampoline_table_free (table);
348     }
349   else if (ffi_trampoline_tables != table)
350     {
351       /* Otherwise, bump this table to the top of the list */
352       table->prev = NULL;
353       table->next = ffi_trampoline_tables;
354       if (ffi_trampoline_tables != NULL)
355         ffi_trampoline_tables->prev = table;
356
357       ffi_trampoline_tables = table;
358     }
359
360   pthread_mutex_unlock (&ffi_trampoline_lock);
361
362   /* Free the closure */
363   free (closure);
364 }
365
366 #endif
367
368 // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
369
370 #elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
371
372 #define USE_LOCKS 1
373 #define USE_DL_PREFIX 1
374 #ifdef __GNUC__
375 #ifndef USE_BUILTIN_FFS
376 #define USE_BUILTIN_FFS 1
377 #endif
378 #endif
379
380 /* We need to use mmap, not sbrk.  */
381 #define HAVE_MORECORE 0
382
383 /* We could, in theory, support mremap, but it wouldn't buy us anything.  */
384 #define HAVE_MREMAP 0
385
386 /* We have no use for this, so save some code and data.  */
387 #define NO_MALLINFO 1
388
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
392
393 /* Don't allocate more than a page unless needed.  */
394 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
395
396 #include <sys/types.h>
397 #include <sys/stat.h>
398 #include <fcntl.h>
399 #include <errno.h>
400 #ifndef _MSC_VER
401 #include <unistd.h>
402 #endif
403 #include <string.h>
404 #include <stdio.h>
405 #if !defined(_WIN32)
406 #ifdef HAVE_MNTENT
407 #include <mntent.h>
408 #endif /* HAVE_MNTENT */
409 #include <sys/param.h>
410 #include <pthread.h>
411
412 /* We don't want sys/mman.h to be included after we redefine mmap and
413    dlmunmap.  */
414 #include <sys/mman.h>
415 #define LACKS_SYS_MMAN_H 1
416
417 #if FFI_MMAP_EXEC_SELINUX
418 #include <sys/statfs.h>
419 #include <stdlib.h>
420
421 static int selinux_enabled = -1;
422
423 static int
424 selinux_enabled_check (void)
425 {
426   struct statfs sfs;
427   FILE *f;
428   char *buf = NULL;
429   size_t len = 0;
430
431   if (statfs ("/selinux", &sfs) >= 0
432       && (unsigned int) sfs.f_type == 0xf97cff8cU)
433     return 1;
434   f = fopen ("/proc/mounts", "r");
435   if (f == NULL)
436     return 0;
437   while (getline (&buf, &len, f) >= 0)
438     {
439       char *p = strchr (buf, ' ');
440       if (p == NULL)
441         break;
442       p = strchr (p + 1, ' ');
443       if (p == NULL)
444         break;
445       if (strncmp (p + 1, "selinuxfs ", 10) == 0)
446         {
447           free (buf);
448           fclose (f);
449           return 1;
450         }
451     }
452   free (buf);
453   fclose (f);
454   return 0;
455 }
456
457 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
458                               : (selinux_enabled = selinux_enabled_check ()))
459
460 #else
461
462 #define is_selinux_enabled() 0
463
464 #endif /* !FFI_MMAP_EXEC_SELINUX */
465
466 /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
467 #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
468 #include <stdlib.h>
469
470 static int emutramp_enabled = -1;
471
472 static int
473 emutramp_enabled_check (void)
474 {
475   char *buf = NULL;
476   size_t len = 0;
477   FILE *f;
478   int ret;
479   f = fopen ("/proc/self/status", "r");
480   if (f == NULL)
481     return 0;
482   ret = 0;
483
484   while (getline (&buf, &len, f) != -1)
485     if (!strncmp (buf, "PaX:", 4))
486       {
487         char emutramp;
488         if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
489           ret = (emutramp == 'E');
490         break;
491       }
492   free (buf);
493   fclose (f);
494   return ret;
495 }
496
497 #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
498                                : (emutramp_enabled = emutramp_enabled_check ()))
499 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
500
501 #elif defined (__CYGWIN__) || defined(__INTERIX)
502
503 #include <sys/mman.h>
504
505 /* Cygwin is Linux-like, but not quite that Linux-like.  */
506 #define is_selinux_enabled() 0
507
508 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
509
510 #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
511 #define is_emutramp_enabled() 0
512 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
513
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;
530
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) */
536
537 #define mmap dlmmap
538 #define munmap dlmunmap
539
540 #include "dlmalloc.c"
541
542 #undef mmap
543 #undef munmap
544
545 #if !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
546
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;
549
550 /* A file descriptor of a temporary file from which we'll map
551    executable pages.  */
552 static int execfd = -1;
553
554 /* The amount of space already allocated from the temporary file.  */
555 static size_t execsize = 0;
556
557 #ifdef HAVE_MEMFD_CREATE
558 /* Open a temporary file name, and immediately unlink it.  */
559 static int
560 open_temp_exec_file_memfd (const char *name)
561 {
562   int fd;
563   fd = memfd_create (name, MFD_CLOEXEC);
564   return fd;
565 }
566 #endif
567
568 /* Open a temporary file name, and immediately unlink it.  */
569 static int
570 open_temp_exec_file_name (char *name, int flags)
571 {
572   int fd;
573
574 #ifdef HAVE_MKOSTEMP
575   fd = mkostemp (name, flags);
576 #else
577   fd = mkstemp (name);
578 #endif
579
580   if (fd != -1)
581     unlink (name);
582
583   return fd;
584 }
585
586 /* Open a temporary file in the named directory.  */
587 static int
588 open_temp_exec_file_dir (const char *dir)
589 {
590   static const char suffix[] = "/ffiXXXXXX";
591   int lendir, flags;
592   char *tempname;
593 #ifdef O_TMPFILE
594   int fd;
595 #endif
596
597 #ifdef O_CLOEXEC
598   flags = O_CLOEXEC;
599 #else
600   flags = 0;
601 #endif
602
603 #ifdef O_TMPFILE
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)) {
607     return fd;
608   } else {
609     errno = 0;
610   }
611 #endif
612
613   lendir = (int) strlen (dir);
614   tempname = __builtin_alloca (lendir + sizeof (suffix));
615
616   if (!tempname)
617     return -1;
618
619   memcpy (tempname, dir, lendir);
620   memcpy (tempname + lendir, suffix, sizeof (suffix));
621
622   return open_temp_exec_file_name (tempname, flags);
623 }
624
625 /* Open a temporary file in the directory in the named environment
626    variable.  */
627 static int
628 open_temp_exec_file_env (const char *envvar)
629 {
630   const char *value = getenv (envvar);
631
632   if (!value)
633     return -1;
634
635   return open_temp_exec_file_dir (value);
636 }
637
638 #ifdef HAVE_MNTENT
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.  */
643 static int
644 open_temp_exec_file_mnt (const char *mounts)
645 {
646   static const char *last_mounts;
647   static FILE *last_mntent;
648
649   if (mounts != last_mounts)
650     {
651       if (last_mntent)
652         endmntent (last_mntent);
653
654       last_mounts = mounts;
655
656       if (mounts)
657         last_mntent = setmntent (mounts, "r");
658       else
659         last_mntent = NULL;
660     }
661
662   if (!last_mntent)
663     return -1;
664
665   for (;;)
666     {
667       int fd;
668       struct mntent mnt;
669       char buf[MAXPATHLEN * 3];
670
671       if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
672         return -1;
673
674       if (hasmntopt (&mnt, "ro")
675           || hasmntopt (&mnt, "noexec")
676           || access (mnt.mnt_dir, W_OK))
677         continue;
678
679       fd = open_temp_exec_file_dir (mnt.mnt_dir);
680
681       if (fd != -1)
682         return fd;
683     }
684 }
685 #endif /* HAVE_MNTENT */
686
687 /* Instructions to look for a location to hold a temporary file that
688    can be mapped in for execution.  */
689 static struct
690 {
691   int (*func)(const char *);
692   const char *arg;
693   int repeat;
694 } open_temp_exec_file_opts[] = {
695 #ifdef HAVE_MEMFD_CREATE
696   { open_temp_exec_file_memfd, "libffi", 0 },
697 #endif
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 },
703 #ifdef HAVE_MNTENT
704   { open_temp_exec_file_mnt, "/etc/mtab", 1 },
705   { open_temp_exec_file_mnt, "/proc/mounts", 1 },
706 #endif /* HAVE_MNTENT */
707 };
708
709 /* Current index into open_temp_exec_file_opts.  */
710 static int open_temp_exec_file_opts_idx = 0;
711
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.  */
715 static int
716 open_temp_exec_file_opts_next (void)
717 {
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);
720
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)))
725     {
726       open_temp_exec_file_opts_idx = 0;
727       return 1;
728     }
729
730   return 0;
731 }
732
733 /* Return a file descriptor of a temporary zero-sized file in a
734    writable and executable filesystem.  */
735 static int
736 open_temp_exec_file (void)
737 {
738   int fd;
739
740   do
741     {
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);
744
745       if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
746           || fd == -1)
747         {
748           if (open_temp_exec_file_opts_next ())
749             break;
750         }
751     }
752   while (fd == -1);
753
754   return fd;
755 }
756
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.  */
764 static int
765 allocate_space (int fd, off_t offset, off_t len)
766 {
767   static size_t page_size;
768
769   /* Obtain system page size. */
770   if (!page_size)
771     page_size = sysconf(_SC_PAGESIZE);
772
773   unsigned char buf[page_size];
774   memset (buf, 0, page_size);
775
776   while (len > 0)
777     {
778       off_t to_write = (len < page_size) ? len : page_size;
779       if (write (fd, buf, to_write) < to_write)
780         return -1;
781       len -= to_write;
782     }
783
784   return 0;
785 }
786
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.  */
792 static void *
793 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
794 {
795   void *ptr;
796
797   if (execfd == -1)
798     {
799       open_temp_exec_file_opts_idx = 0;
800     retry_open:
801       execfd = open_temp_exec_file ();
802       if (execfd == -1)
803         return MFAIL;
804     }
805
806   offset = execsize;
807
808   if (allocate_space (execfd, offset, length))
809     return MFAIL;
810
811   flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
812   flags |= MAP_SHARED;
813
814   ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
815               flags, execfd, offset);
816   if (ptr == MFAIL)
817     {
818       if (!offset)
819         {
820           close (execfd);
821           goto retry_open;
822         }
823       if (ftruncate (execfd, offset) != 0)
824       {
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. */
828       }
829
830       return MFAIL;
831     }
832   else if (!offset
833            && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
834     open_temp_exec_file_opts_next ();
835
836   start = mmap (start, length, prot, flags, execfd, offset);
837
838   if (start == MFAIL)
839     {
840       munmap (ptr, length);
841       if (ftruncate (execfd, offset) != 0)
842       {
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. */
846       }
847       return start;
848     }
849
850   mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
851
852   execsize += length;
853
854   return start;
855 }
856
857 /* Map in a writable and executable chunk of memory if possible.
858    Failing that, fall back to dlmmap_locked.  */
859 static void *
860 dlmmap (void *start, size_t length, int prot,
861         int flags, int fd, off_t offset)
862 {
863   void *ptr;
864
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);
869
870   if (execfd == -1 && ffi_tramp_is_supported ())
871     {
872       ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
873       return ptr;
874     }
875
876   if (execfd == -1 && is_emutramp_enabled ())
877     {
878       ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
879       return ptr;
880     }
881
882   if (execfd == -1 && !is_selinux_enabled ())
883     {
884       ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
885
886       if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
887         /* Cool, no need to mess with separate segments.  */
888         return ptr;
889
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.  */
893     }
894
895   if (execsize == 0 || execfd == -1)
896     {
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);
900
901       return ptr;
902     }
903
904   return dlmmap_locked (start, length, prot, flags, offset);
905 }
906
907 /* Release memory at the given address, as well as the corresponding
908    executable page if it's separate.  */
909 static int
910 dlmunmap (void *start, size_t length)
911 {
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.
917      Yuck.  */
918   msegmentptr seg = segment_holding (gm, start);
919   void *code;
920
921   if (seg && (code = add_segment_exec_offset (start, seg)) != start)
922     {
923       int ret = munmap (code, length);
924       if (ret)
925         return ret;
926     }
927
928   return munmap (start, length);
929 }
930
931 #if FFI_CLOSURE_FREE_CODE
932 /* Return segment holding given code address.  */
933 static msegmentptr
934 segment_holding_code (mstate m, char* addr)
935 {
936   msegmentptr sp = &m->seg;
937   for (;;) {
938     if (addr >= add_segment_exec_offset (sp->base, sp)
939         && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
940       return sp;
941     if ((sp = sp->next) == 0)
942       return 0;
943   }
944 }
945 #endif
946
947 #endif /* !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
948
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.  */
952 void *
953 ffi_closure_alloc (size_t size, void **code)
954 {
955   void *ptr, *ftramp;
956
957   if (!code)
958     return NULL;
959
960   ptr = FFI_CLOSURE_PTR (dlmalloc (size));
961
962   if (ptr)
963     {
964       msegmentptr seg = segment_holding (gm, ptr);
965
966       *code = add_segment_exec_offset (ptr, seg);
967       if (!ffi_tramp_is_supported ())
968         return ptr;
969
970       ftramp = ffi_tramp_alloc (0);
971       if (ftramp == NULL)
972       {
973         dlfree (FFI_RESTORE_PTR (ptr));
974         return NULL;
975       }
976       *code = ffi_tramp_get_addr (ftramp);
977       ((ffi_closure *) ptr)->ftramp = ftramp;
978     }
979
980   return ptr;
981 }
982
983 void *
984 ffi_data_to_code_pointer (void *data)
985 {
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. */
991   if (seg)
992     {
993       if (!ffi_tramp_is_supported ())
994         return add_segment_exec_offset (data, seg);
995       return ffi_tramp_get_addr (((ffi_closure *) data)->ftramp);
996     }
997   else
998     return data;
999 }
1000
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.  */
1005 void
1006 ffi_closure_free (void *ptr)
1007 {
1008 #if FFI_CLOSURE_FREE_CODE
1009   msegmentptr seg = segment_holding_code (gm, ptr);
1010
1011   if (seg)
1012     ptr = sub_segment_exec_offset (ptr, seg);
1013 #endif
1014   if (ffi_tramp_is_supported ())
1015     ffi_tramp_free (((ffi_closure *) ptr)->ftramp);
1016
1017   dlfree (FFI_RESTORE_PTR (ptr));
1018 }
1019
1020 int
1021 ffi_tramp_is_present (void *ptr)
1022 {
1023   msegmentptr seg = segment_holding (gm, ptr);
1024   return seg != NULL && ffi_tramp_is_supported();
1025 }
1026
1027 # else /* ! FFI_MMAP_EXEC_WRIT */
1028
1029 /* On many systems, memory returned by malloc is writable and
1030    executable, so just use it.  */
1031
1032 #include <stdlib.h>
1033
1034 void *
1035 ffi_closure_alloc (size_t size, void **code)
1036 {
1037   if (!code)
1038     return NULL;
1039
1040   return *code = FFI_CLOSURE_PTR (malloc (size));
1041 }
1042
1043 void
1044 ffi_closure_free (void *ptr)
1045 {
1046   free (FFI_RESTORE_PTR (ptr));
1047 }
1048
1049 void *
1050 ffi_data_to_code_pointer (void *data)
1051 {
1052   return data;
1053 }
1054
1055 int
1056 ffi_tramp_is_present (__attribute__((unused)) void *ptr)
1057 {
1058   return 0;
1059 }
1060
1061 # endif /* ! FFI_MMAP_EXEC_WRIT */
1062 #endif /* FFI_CLOSURES */
1063
1064 #endif /* NetBSD with PROT_MPROTECT */