(root)/
gcc-13.2.0/
libffi/
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  
     177    /* free list tracking */
     178    uint16_t free_count;
     179    ffi_trampoline_table_entry *free_list;
     180    ffi_trampoline_table_entry *free_list_pool;
     181  
     182    ffi_trampoline_table *prev;
     183    ffi_trampoline_table *next;
     184  };
     185  
     186  struct ffi_trampoline_table_entry
     187  {
     188    void *(*trampoline) (void);
     189    ffi_trampoline_table_entry *next;
     190  };
     191  
     192  /* Total number of trampolines that fit in one trampoline table */
     193  #define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
     194  
     195  static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
     196  static ffi_trampoline_table *ffi_trampoline_tables = NULL;
     197  
     198  static ffi_trampoline_table *
     199  ffi_trampoline_table_alloc (void)
     200  {
     201    ffi_trampoline_table *table;
     202    vm_address_t config_page;
     203    vm_address_t trampoline_page;
     204    vm_address_t trampoline_page_template;
     205    vm_prot_t cur_prot;
     206    vm_prot_t max_prot;
     207    kern_return_t kt;
     208    uint16_t i;
     209  
     210    /* Allocate two pages -- a config page and a placeholder page */
     211    config_page = 0x0;
     212    kt = vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
     213  		    VM_FLAGS_ANYWHERE);
     214    if (kt != KERN_SUCCESS)
     215      return NULL;
     216  
     217    /* Remap the trampoline table on top of the placeholder page */
     218    trampoline_page = config_page + PAGE_MAX_SIZE;
     219  
     220  #ifdef HAVE_PTRAUTH
     221    trampoline_page_template = (vm_address_t)(uintptr_t)ptrauth_auth_data((void *)&ffi_closure_trampoline_table_page, ptrauth_key_function_pointer, 0);
     222  #else
     223    trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
     224  #endif
     225  
     226  #ifdef __arm__
     227    /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
     228    trampoline_page_template &= ~1UL;
     229  #endif
     230    kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0,
     231  		 VM_FLAGS_OVERWRITE, mach_task_self (), trampoline_page_template,
     232  		 FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
     233    if (kt != KERN_SUCCESS || !(cur_prot & VM_PROT_EXECUTE))
     234      {
     235        vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
     236        return NULL;
     237      }
     238  
     239    /* We have valid trampoline and config pages */
     240    table = calloc (1, sizeof (ffi_trampoline_table));
     241    table->free_count = FFI_TRAMPOLINE_COUNT;
     242    table->config_page = config_page;
     243  
     244    /* Create and initialize the free list */
     245    table->free_list_pool =
     246      calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
     247  
     248    for (i = 0; i < table->free_count; i++)
     249      {
     250        ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
     251        entry->trampoline =
     252  	(void *) (trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
     253  #ifdef HAVE_PTRAUTH
     254        entry->trampoline = ptrauth_sign_unauthenticated(entry->trampoline, ptrauth_key_function_pointer, 0);
     255  #endif
     256  
     257        if (i < table->free_count - 1)
     258  	entry->next = &table->free_list_pool[i + 1];
     259      }
     260  
     261    table->free_list = table->free_list_pool;
     262  
     263    return table;
     264  }
     265  
     266  static void
     267  ffi_trampoline_table_free (ffi_trampoline_table *table)
     268  {
     269    /* Remove from the list */
     270    if (table->prev != NULL)
     271      table->prev->next = table->next;
     272  
     273    if (table->next != NULL)
     274      table->next->prev = table->prev;
     275  
     276    /* Deallocate pages */
     277    vm_deallocate (mach_task_self (), table->config_page, PAGE_MAX_SIZE * 2);
     278  
     279    /* Deallocate free list */
     280    free (table->free_list_pool);
     281    free (table);
     282  }
     283  
     284  void *
     285  ffi_closure_alloc (size_t size, void **code)
     286  {
     287    /* Create the closure */
     288    ffi_closure *closure = malloc (size);
     289    if (closure == NULL)
     290      return NULL;
     291  
     292    pthread_mutex_lock (&ffi_trampoline_lock);
     293  
     294    /* Check for an active trampoline table with available entries. */
     295    ffi_trampoline_table *table = ffi_trampoline_tables;
     296    if (table == NULL || table->free_list == NULL)
     297      {
     298        table = ffi_trampoline_table_alloc ();
     299        if (table == NULL)
     300  	{
     301  	  pthread_mutex_unlock (&ffi_trampoline_lock);
     302  	  free (closure);
     303  	  return NULL;
     304  	}
     305  
     306        /* Insert the new table at the top of the list */
     307        table->next = ffi_trampoline_tables;
     308        if (table->next != NULL)
     309  	table->next->prev = table;
     310  
     311        ffi_trampoline_tables = table;
     312      }
     313  
     314    /* Claim the free entry */
     315    ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
     316    ffi_trampoline_tables->free_list = entry->next;
     317    ffi_trampoline_tables->free_count--;
     318    entry->next = NULL;
     319  
     320    pthread_mutex_unlock (&ffi_trampoline_lock);
     321  
     322    /* Initialize the return values */
     323    *code = entry->trampoline;
     324    closure->trampoline_table = table;
     325    closure->trampoline_table_entry = entry;
     326  
     327    return closure;
     328  }
     329  
     330  void
     331  ffi_closure_free (void *ptr)
     332  {
     333    ffi_closure *closure = ptr;
     334  
     335    pthread_mutex_lock (&ffi_trampoline_lock);
     336  
     337    /* Fetch the table and entry references */
     338    ffi_trampoline_table *table = closure->trampoline_table;
     339    ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
     340  
     341    /* Return the entry to the free list */
     342    entry->next = table->free_list;
     343    table->free_list = entry;
     344    table->free_count++;
     345  
     346    /* If all trampolines within this table are free, and at least one other table exists, deallocate
     347     * the table */
     348    if (table->free_count == FFI_TRAMPOLINE_COUNT
     349        && ffi_trampoline_tables != table)
     350      {
     351        ffi_trampoline_table_free (table);
     352      }
     353    else if (ffi_trampoline_tables != table)
     354      {
     355        /* Otherwise, bump this table to the top of the list */
     356        table->prev = NULL;
     357        table->next = ffi_trampoline_tables;
     358        if (ffi_trampoline_tables != NULL)
     359  	ffi_trampoline_tables->prev = table;
     360  
     361        ffi_trampoline_tables = table;
     362      }
     363  
     364    pthread_mutex_unlock (&ffi_trampoline_lock);
     365  
     366    /* Free the closure */
     367    free (closure);
     368  }
     369  
     370  #endif
     371  
     372  // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
     373  
     374  #elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
     375  
     376  #define USE_LOCKS 1
     377  #define USE_DL_PREFIX 1
     378  #ifdef __GNUC__
     379  #ifndef USE_BUILTIN_FFS
     380  #define USE_BUILTIN_FFS 1
     381  #endif
     382  #endif
     383  
     384  /* We need to use mmap, not sbrk.  */
     385  #define HAVE_MORECORE 0
     386  
     387  /* We could, in theory, support mremap, but it wouldn't buy us anything.  */
     388  #define HAVE_MREMAP 0
     389  
     390  /* We have no use for this, so save some code and data.  */
     391  #define NO_MALLINFO 1
     392  
     393  /* We need all allocations to be in regular segments, otherwise we
     394     lose track of the corresponding code address.  */
     395  #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
     396  
     397  /* Don't allocate more than a page unless needed.  */
     398  #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
     399  
     400  #include <sys/types.h>
     401  #include <sys/stat.h>
     402  #include <fcntl.h>
     403  #include <errno.h>
     404  #ifndef _MSC_VER
     405  #include <unistd.h>
     406  #endif
     407  #include <string.h>
     408  #include <stdio.h>
     409  #if !defined(_WIN32)
     410  #ifdef HAVE_MNTENT
     411  #include <mntent.h>
     412  #endif /* HAVE_MNTENT */
     413  #include <sys/param.h>
     414  #include <pthread.h>
     415  
     416  /* We don't want sys/mman.h to be included after we redefine mmap and
     417     dlmunmap.  */
     418  #include <sys/mman.h>
     419  #define LACKS_SYS_MMAN_H 1
     420  
     421  #if FFI_MMAP_EXEC_SELINUX
     422  #include <sys/statfs.h>
     423  #include <stdlib.h>
     424  
     425  static int selinux_enabled = -1;
     426  
     427  static int
     428  selinux_enabled_check (void)
     429  {
     430    struct statfs sfs;
     431    FILE *f;
     432    char *buf = NULL;
     433    size_t len = 0;
     434  
     435    if (statfs ("/selinux", &sfs) >= 0
     436        && (unsigned int) sfs.f_type == 0xf97cff8cU)
     437      return 1;
     438    f = fopen ("/proc/mounts", "r");
     439    if (f == NULL)
     440      return 0;
     441    while (getline (&buf, &len, f) >= 0)
     442      {
     443        char *p = strchr (buf, ' ');
     444        if (p == NULL)
     445          break;
     446        p = strchr (p + 1, ' ');
     447        if (p == NULL)
     448          break;
     449        if (strncmp (p + 1, "selinuxfs ", 10) == 0)
     450          {
     451            free (buf);
     452            fclose (f);
     453            return 1;
     454          }
     455      }
     456    free (buf);
     457    fclose (f);
     458    return 0;
     459  }
     460  
     461  #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
     462  			      : (selinux_enabled = selinux_enabled_check ()))
     463  
     464  #else
     465  
     466  #define is_selinux_enabled() 0
     467  
     468  #endif /* !FFI_MMAP_EXEC_SELINUX */
     469  
     470  /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
     471  #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
     472  #include <stdlib.h>
     473  
     474  static int emutramp_enabled = -1;
     475  
     476  static int
     477  emutramp_enabled_check (void)
     478  {
     479    char *buf = NULL;
     480    size_t len = 0;
     481    FILE *f;
     482    int ret;
     483    f = fopen ("/proc/self/status", "r");
     484    if (f == NULL)
     485      return 0;
     486    ret = 0;
     487  
     488    while (getline (&buf, &len, f) != -1)
     489      if (!strncmp (buf, "PaX:", 4))
     490        {
     491          char emutramp;
     492          if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
     493            ret = (emutramp == 'E');
     494          break;
     495        }
     496    free (buf);
     497    fclose (f);
     498    return ret;
     499  }
     500  
     501  #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
     502                                 : (emutramp_enabled = emutramp_enabled_check ()))
     503  #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
     504  
     505  #elif defined (__CYGWIN__) || defined(__INTERIX)
     506  
     507  #include <sys/mman.h>
     508  
     509  /* Cygwin is Linux-like, but not quite that Linux-like.  */
     510  #define is_selinux_enabled() 0
     511  
     512  #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
     513  
     514  #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
     515  #define is_emutramp_enabled() 0
     516  #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
     517  
     518  /* Declare all functions defined in dlmalloc.c as static.  */
     519  static void *dlmalloc(size_t);
     520  static void dlfree(void*);
     521  static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
     522  static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
     523  static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
     524  static void *dlvalloc(size_t) MAYBE_UNUSED;
     525  static int dlmallopt(int, int) MAYBE_UNUSED;
     526  static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
     527  static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
     528  static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
     529  static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
     530  static void *dlpvalloc(size_t) MAYBE_UNUSED;
     531  static int dlmalloc_trim(size_t) MAYBE_UNUSED;
     532  static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
     533  static void dlmalloc_stats(void) MAYBE_UNUSED;
     534  
     535  #if !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
     536  /* Use these for mmap and munmap within dlmalloc.c.  */
     537  static void *dlmmap(void *, size_t, int, int, int, off_t);
     538  static int dlmunmap(void *, size_t);
     539  #endif /* !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
     540  
     541  #define mmap dlmmap
     542  #define munmap dlmunmap
     543  
     544  #include "dlmalloc.c"
     545  
     546  #undef mmap
     547  #undef munmap
     548  
     549  #if !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
     550  
     551  /* A mutex used to synchronize access to *exec* variables in this file.  */
     552  static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
     553  
     554  /* A file descriptor of a temporary file from which we'll map
     555     executable pages.  */
     556  static int execfd = -1;
     557  
     558  /* The amount of space already allocated from the temporary file.  */
     559  static size_t execsize = 0;
     560  
     561  #ifdef HAVE_MEMFD_CREATE
     562  /* Open a temporary file name, and immediately unlink it.  */
     563  static int
     564  open_temp_exec_file_memfd (const char *name)
     565  {
     566    int fd;
     567    fd = memfd_create (name, MFD_CLOEXEC);
     568    return fd;
     569  }
     570  #endif
     571  
     572  /* Open a temporary file name, and immediately unlink it.  */
     573  static int
     574  open_temp_exec_file_name (char *name, int flags)
     575  {
     576    int fd;
     577  
     578  #ifdef HAVE_MKOSTEMP
     579    fd = mkostemp (name, flags);
     580  #else
     581    fd = mkstemp (name);
     582  #endif
     583  
     584    if (fd != -1)
     585      unlink (name);
     586  
     587    return fd;
     588  }
     589  
     590  /* Open a temporary file in the named directory.  */
     591  static int
     592  open_temp_exec_file_dir (const char *dir)
     593  {
     594    static const char suffix[] = "/ffiXXXXXX";
     595    int lendir, flags;
     596    char *tempname;
     597  #ifdef O_TMPFILE
     598    int fd;
     599  #endif
     600  
     601  #ifdef O_CLOEXEC
     602    flags = O_CLOEXEC;
     603  #else
     604    flags = 0;
     605  #endif
     606  
     607  #ifdef O_TMPFILE
     608    fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
     609    /* If the running system does not support the O_TMPFILE flag then retry without it. */
     610    if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
     611      return fd;
     612    } else {
     613      errno = 0;
     614    }
     615  #endif
     616  
     617    lendir = (int) strlen (dir);
     618    tempname = __builtin_alloca (lendir + sizeof (suffix));
     619  
     620    if (!tempname)
     621      return -1;
     622  
     623    memcpy (tempname, dir, lendir);
     624    memcpy (tempname + lendir, suffix, sizeof (suffix));
     625  
     626    return open_temp_exec_file_name (tempname, flags);
     627  }
     628  
     629  /* Open a temporary file in the directory in the named environment
     630     variable.  */
     631  static int
     632  open_temp_exec_file_env (const char *envvar)
     633  {
     634    const char *value = getenv (envvar);
     635  
     636    if (!value)
     637      return -1;
     638  
     639    return open_temp_exec_file_dir (value);
     640  }
     641  
     642  #ifdef HAVE_MNTENT
     643  /* Open a temporary file in an executable and writable mount point
     644     listed in the mounts file.  Subsequent calls with the same mounts
     645     keep searching for mount points in the same file.  Providing NULL
     646     as the mounts file closes the file.  */
     647  static int
     648  open_temp_exec_file_mnt (const char *mounts)
     649  {
     650    static const char *last_mounts;
     651    static FILE *last_mntent;
     652  
     653    if (mounts != last_mounts)
     654      {
     655        if (last_mntent)
     656  	endmntent (last_mntent);
     657  
     658        last_mounts = mounts;
     659  
     660        if (mounts)
     661  	last_mntent = setmntent (mounts, "r");
     662        else
     663  	last_mntent = NULL;
     664      }
     665  
     666    if (!last_mntent)
     667      return -1;
     668  
     669    for (;;)
     670      {
     671        int fd;
     672        struct mntent mnt;
     673        char buf[MAXPATHLEN * 3];
     674  
     675        if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
     676  	return -1;
     677  
     678        if (hasmntopt (&mnt, "ro")
     679  	  || hasmntopt (&mnt, "noexec")
     680  	  || access (mnt.mnt_dir, W_OK))
     681  	continue;
     682  
     683        fd = open_temp_exec_file_dir (mnt.mnt_dir);
     684  
     685        if (fd != -1)
     686  	return fd;
     687      }
     688  }
     689  #endif /* HAVE_MNTENT */
     690  
     691  /* Instructions to look for a location to hold a temporary file that
     692     can be mapped in for execution.  */
     693  static struct
     694  {
     695    int (*func)(const char *);
     696    const char *arg;
     697    int repeat;
     698  } open_temp_exec_file_opts[] = {
     699  #ifdef HAVE_MEMFD_CREATE
     700    { open_temp_exec_file_memfd, "libffi", 0 },
     701  #endif
     702    { open_temp_exec_file_env, "LIBFFI_TMPDIR", 0 },
     703    { open_temp_exec_file_env, "TMPDIR", 0 },
     704    { open_temp_exec_file_dir, "/tmp", 0 },
     705    { open_temp_exec_file_dir, "/var/tmp", 0 },
     706    { open_temp_exec_file_dir, "/dev/shm", 0 },
     707    { open_temp_exec_file_env, "HOME", 0 },
     708  #ifdef HAVE_MNTENT
     709    { open_temp_exec_file_mnt, "/etc/mtab", 1 },
     710    { open_temp_exec_file_mnt, "/proc/mounts", 1 },
     711  #endif /* HAVE_MNTENT */
     712  };
     713  
     714  /* Current index into open_temp_exec_file_opts.  */
     715  static int open_temp_exec_file_opts_idx = 0;
     716  
     717  /* Reset a current multi-call func, then advances to the next entry.
     718     If we're at the last, go back to the first and return nonzero,
     719     otherwise return zero.  */
     720  static int
     721  open_temp_exec_file_opts_next (void)
     722  {
     723    if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
     724      open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
     725  
     726    open_temp_exec_file_opts_idx++;
     727    if (open_temp_exec_file_opts_idx
     728        == (sizeof (open_temp_exec_file_opts)
     729  	  / sizeof (*open_temp_exec_file_opts)))
     730      {
     731        open_temp_exec_file_opts_idx = 0;
     732        return 1;
     733      }
     734  
     735    return 0;
     736  }
     737  
     738  /* Return a file descriptor of a temporary zero-sized file in a
     739     writable and executable filesystem.  */
     740  static int
     741  open_temp_exec_file (void)
     742  {
     743    int fd;
     744  
     745    do
     746      {
     747        fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
     748  	(open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
     749  
     750        if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
     751  	  || fd == -1)
     752  	{
     753  	  if (open_temp_exec_file_opts_next ())
     754  	    break;
     755  	}
     756      }
     757    while (fd == -1);
     758  
     759    return fd;
     760  }
     761  
     762  /* We need to allocate space in a file that will be backing a writable
     763     mapping.  Several problems exist with the usual approaches:
     764     - fallocate() is Linux-only
     765     - posix_fallocate() is not available on all platforms
     766     - ftruncate() does not allocate space on filesystems with sparse files
     767     Failure to allocate the space will cause SIGBUS to be thrown when
     768     the mapping is subsequently written to.  */
     769  static int
     770  allocate_space (int fd, off_t offset, off_t len)
     771  {
     772    static size_t page_size;
     773  
     774    /* Obtain system page size. */
     775    if (!page_size)
     776      page_size = sysconf(_SC_PAGESIZE);
     777  
     778    unsigned char buf[page_size];
     779    memset (buf, 0, page_size);
     780  
     781    while (len > 0)
     782      {
     783        off_t to_write = (len < page_size) ? len : page_size;
     784        if (write (fd, buf, to_write) < to_write)
     785          return -1;
     786        len -= to_write;
     787      }
     788  
     789    return 0;
     790  }
     791  
     792  /* Map in a chunk of memory from the temporary exec file into separate
     793     locations in the virtual memory address space, one writable and one
     794     executable.  Returns the address of the writable portion, after
     795     storing an offset to the corresponding executable portion at the
     796     last word of the requested chunk.  */
     797  static void *
     798  dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
     799  {
     800    void *ptr;
     801  
     802    if (execfd == -1)
     803      {
     804        open_temp_exec_file_opts_idx = 0;
     805      retry_open:
     806        execfd = open_temp_exec_file ();
     807        if (execfd == -1)
     808  	return MFAIL;
     809      }
     810  
     811    offset = execsize;
     812  
     813    if (allocate_space (execfd, offset, length))
     814      return MFAIL;
     815  
     816    flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
     817    flags |= MAP_SHARED;
     818  
     819    ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
     820  	      flags, execfd, offset);
     821    if (ptr == MFAIL)
     822      {
     823        if (!offset)
     824  	{
     825  	  close (execfd);
     826  	  goto retry_open;
     827  	}
     828        if (ftruncate (execfd, offset) != 0)
     829        {
     830          /* Fixme : Error logs can be added here. Returning an error for
     831           * ftruncte() will not add any advantage as it is being
     832           * validating in the error case. */
     833        }
     834  
     835        return MFAIL;
     836      }
     837    else if (!offset
     838  	   && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
     839      open_temp_exec_file_opts_next ();
     840  
     841    start = mmap (start, length, prot, flags, execfd, offset);
     842  
     843    if (start == MFAIL)
     844      {
     845        munmap (ptr, length);
     846        if (ftruncate (execfd, offset) != 0)
     847        {
     848          /* Fixme : Error logs can be added here. Returning an error for
     849           * ftruncte() will not add any advantage as it is being
     850           * validating in the error case. */
     851        }
     852        return start;
     853      }
     854  
     855    mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
     856  
     857    execsize += length;
     858  
     859    return start;
     860  }
     861  
     862  /* Map in a writable and executable chunk of memory if possible.
     863     Failing that, fall back to dlmmap_locked.  */
     864  static void *
     865  dlmmap (void *start, size_t length, int prot,
     866  	int flags, int fd, off_t offset)
     867  {
     868    void *ptr;
     869  
     870    assert (start == NULL && length % malloc_getpagesize == 0
     871  	  && prot == (PROT_READ | PROT_WRITE)
     872  	  && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
     873  	  && fd == -1 && offset == 0);
     874  
     875    if (execfd == -1 && ffi_tramp_is_supported ())
     876      {
     877        ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
     878        return ptr;
     879      }
     880  
     881    if (execfd == -1 && is_emutramp_enabled ())
     882      {
     883        ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
     884        return ptr;
     885      }
     886  
     887    if (execfd == -1 && !is_selinux_enabled ())
     888      {
     889        ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
     890  
     891        if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
     892  	/* Cool, no need to mess with separate segments.  */
     893  	return ptr;
     894  
     895        /* If MREMAP_DUP is ever introduced and implemented, try mmap
     896  	 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
     897  	 MREMAP_DUP and prot at this point.  */
     898      }
     899  
     900    if (execsize == 0 || execfd == -1)
     901      {
     902        pthread_mutex_lock (&open_temp_exec_file_mutex);
     903        ptr = dlmmap_locked (start, length, prot, flags, offset);
     904        pthread_mutex_unlock (&open_temp_exec_file_mutex);
     905  
     906        return ptr;
     907      }
     908  
     909    return dlmmap_locked (start, length, prot, flags, offset);
     910  }
     911  
     912  /* Release memory at the given address, as well as the corresponding
     913     executable page if it's separate.  */
     914  static int
     915  dlmunmap (void *start, size_t length)
     916  {
     917    /* We don't bother decreasing execsize or truncating the file, since
     918       we can't quite tell whether we're unmapping the end of the file.
     919       We don't expect frequent deallocation anyway.  If we did, we
     920       could locate pages in the file by writing to the pages being
     921       deallocated and checking that the file contents change.
     922       Yuck.  */
     923    msegmentptr seg = segment_holding (gm, start);
     924    void *code;
     925  
     926    if (seg && (code = add_segment_exec_offset (start, seg)) != start)
     927      {
     928        int ret = munmap (code, length);
     929        if (ret)
     930  	return ret;
     931      }
     932  
     933    return munmap (start, length);
     934  }
     935  
     936  #if FFI_CLOSURE_FREE_CODE
     937  /* Return segment holding given code address.  */
     938  static msegmentptr
     939  segment_holding_code (mstate m, char* addr)
     940  {
     941    msegmentptr sp = &m->seg;
     942    for (;;) {
     943      if (addr >= add_segment_exec_offset (sp->base, sp)
     944  	&& addr < add_segment_exec_offset (sp->base, sp) + sp->size)
     945        return sp;
     946      if ((sp = sp->next) == 0)
     947        return 0;
     948    }
     949  }
     950  #endif
     951  
     952  #endif /* !(defined(_WIN32) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
     953  
     954  /* Allocate a chunk of memory with the given size.  Returns a pointer
     955     to the writable address, and sets *CODE to the executable
     956     corresponding virtual address.  */
     957  void *
     958  ffi_closure_alloc (size_t size, void **code)
     959  {
     960    void *ptr, *ftramp;
     961  
     962    if (!code)
     963      return NULL;
     964  
     965    ptr = FFI_CLOSURE_PTR (dlmalloc (size));
     966  
     967    if (ptr)
     968      {
     969        msegmentptr seg = segment_holding (gm, ptr);
     970  
     971        *code = add_segment_exec_offset (ptr, seg);
     972        if (!ffi_tramp_is_supported ())
     973          return ptr;
     974  
     975        ftramp = ffi_tramp_alloc (0);
     976        if (ftramp == NULL)
     977        {
     978          dlfree (FFI_RESTORE_PTR (ptr));
     979          return NULL;
     980        }
     981        *code = ffi_tramp_get_addr (ftramp);
     982        ((ffi_closure *) ptr)->ftramp = ftramp;
     983      }
     984  
     985    return ptr;
     986  }
     987  
     988  void *
     989  ffi_data_to_code_pointer (void *data)
     990  {
     991    msegmentptr seg = segment_holding (gm, data);
     992    /* We expect closures to be allocated with ffi_closure_alloc(), in
     993       which case seg will be non-NULL.  However, some users take on the
     994       burden of managing this memory themselves, in which case this
     995       we'll just return data. */
     996    if (seg)
     997      {
     998        if (!ffi_tramp_is_supported ())
     999          return add_segment_exec_offset (data, seg);
    1000        return ffi_tramp_get_addr (((ffi_closure *) data)->ftramp);
    1001      }
    1002    else
    1003      return data;
    1004  }
    1005  
    1006  /* Release a chunk of memory allocated with ffi_closure_alloc.  If
    1007     FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
    1008     writable or the executable address given.  Otherwise, only the
    1009     writable address can be provided here.  */
    1010  void
    1011  ffi_closure_free (void *ptr)
    1012  {
    1013  #if FFI_CLOSURE_FREE_CODE
    1014    msegmentptr seg = segment_holding_code (gm, ptr);
    1015  
    1016    if (seg)
    1017      ptr = sub_segment_exec_offset (ptr, seg);
    1018  #endif
    1019    if (ffi_tramp_is_supported ())
    1020      ffi_tramp_free (((ffi_closure *) ptr)->ftramp);
    1021  
    1022    dlfree (FFI_RESTORE_PTR (ptr));
    1023  }
    1024  
    1025  int
    1026  ffi_tramp_is_present (void *ptr)
    1027  {
    1028    msegmentptr seg = segment_holding (gm, ptr);
    1029    return seg != NULL && ffi_tramp_is_supported();
    1030  }
    1031  
    1032  # else /* ! FFI_MMAP_EXEC_WRIT */
    1033  
    1034  /* On many systems, memory returned by malloc is writable and
    1035     executable, so just use it.  */
    1036  
    1037  #include <stdlib.h>
    1038  
    1039  void *
    1040  ffi_closure_alloc (size_t size, void **code)
    1041  {
    1042    if (!code)
    1043      return NULL;
    1044  
    1045    return *code = FFI_CLOSURE_PTR (malloc (size));
    1046  }
    1047  
    1048  void
    1049  ffi_closure_free (void *ptr)
    1050  {
    1051    free (FFI_RESTORE_PTR (ptr));
    1052  }
    1053  
    1054  void *
    1055  ffi_data_to_code_pointer (void *data)
    1056  {
    1057    return data;
    1058  }
    1059  
    1060  int
    1061  ffi_tramp_is_present (__attribute__((unused)) void *ptr)
    1062  {
    1063    return 0;
    1064  }
    1065  
    1066  # endif /* ! FFI_MMAP_EXEC_WRIT */
    1067  #endif /* FFI_CLOSURES */
    1068  
    1069  #endif /* NetBSD with PROT_MPROTECT */