github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/gofrontend/libffi/src/closures.c (about)

     1  /* -----------------------------------------------------------------------
     2     closures.c - Copyright (c) 2007, 2009, 2010  Red Hat, Inc.
     3                  Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
     4                  Copyright (c) 2011 Plausible Labs Cooperative, Inc.
     5  
     6     Code to allocate and deallocate memory for closures.
     7  
     8     Permission is hereby granted, free of charge, to any person obtaining
     9     a copy of this software and associated documentation files (the
    10     ``Software''), to deal in the Software without restriction, including
    11     without limitation the rights to use, copy, modify, merge, publish,
    12     distribute, sublicense, and/or sell copies of the Software, and to
    13     permit persons to whom the Software is furnished to do so, subject to
    14     the following conditions:
    15  
    16     The above copyright notice and this permission notice shall be included
    17     in all copies or substantial portions of the Software.
    18  
    19     THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
    20     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    21     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    22     NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    23     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    24     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    25     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    26     DEALINGS IN THE SOFTWARE.
    27     ----------------------------------------------------------------------- */
    28  
    29  #if defined __linux__ && !defined _GNU_SOURCE
    30  #define _GNU_SOURCE 1
    31  #endif
    32  
    33  #include <ffi.h>
    34  #include <ffi_common.h>
    35  
    36  #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
    37  # if __gnu_linux__ && !defined(__ANDROID__)
    38  /* This macro indicates it may be forbidden to map anonymous memory
    39     with both write and execute permission.  Code compiled when this
    40     option is defined will attempt to map such pages once, but if it
    41     fails, it falls back to creating a temporary file in a writable and
    42     executable filesystem and mapping pages from it into separate
    43     locations in the virtual memory space, one location writable and
    44     another executable.  */
    45  #  define FFI_MMAP_EXEC_WRIT 1
    46  #  define HAVE_MNTENT 1
    47  # endif
    48  # if defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)
    49  /* Windows systems may have Data Execution Protection (DEP) enabled, 
    50     which requires the use of VirtualMalloc/VirtualFree to alloc/free
    51     executable memory. */
    52  #  define FFI_MMAP_EXEC_WRIT 1
    53  # endif
    54  #endif
    55  
    56  #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
    57  # ifdef __linux__
    58  /* When defined to 1 check for SELinux and if SELinux is active,
    59     don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
    60     might cause audit messages.  */
    61  #  define FFI_MMAP_EXEC_SELINUX 1
    62  # endif
    63  #endif
    64  
    65  #if FFI_CLOSURES
    66  
    67  # if FFI_EXEC_TRAMPOLINE_TABLE
    68  
    69  // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
    70  
    71  # elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
    72  
    73  #define USE_LOCKS 1
    74  #define USE_DL_PREFIX 1
    75  #ifdef __GNUC__
    76  #ifndef USE_BUILTIN_FFS
    77  #define USE_BUILTIN_FFS 1
    78  #endif
    79  #endif
    80  
    81  /* We need to use mmap, not sbrk.  */
    82  #define HAVE_MORECORE 0
    83  
    84  /* We could, in theory, support mremap, but it wouldn't buy us anything.  */
    85  #define HAVE_MREMAP 0
    86  
    87  /* We have no use for this, so save some code and data.  */
    88  #define NO_MALLINFO 1
    89  
    90  /* We need all allocations to be in regular segments, otherwise we
    91     lose track of the corresponding code address.  */
    92  #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
    93  
    94  /* Don't allocate more than a page unless needed.  */
    95  #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
    96  
    97  #if FFI_CLOSURE_TEST
    98  /* Don't release single pages, to avoid a worst-case scenario of
    99     continuously allocating and releasing single pages, but release
   100     pairs of pages, which should do just as well given that allocations
   101     are likely to be small.  */
   102  #define DEFAULT_TRIM_THRESHOLD ((size_t)malloc_getpagesize)
   103  #endif
   104  
   105  #include <sys/types.h>
   106  #include <sys/stat.h>
   107  #include <fcntl.h>
   108  #include <errno.h>
   109  #ifndef _MSC_VER
   110  #include <unistd.h>
   111  #endif
   112  #include <string.h>
   113  #include <stdio.h>
   114  #if !defined(X86_WIN32) && !defined(X86_WIN64)
   115  #ifdef HAVE_MNTENT
   116  #include <mntent.h>
   117  #endif /* HAVE_MNTENT */
   118  #include <sys/param.h>
   119  #include <pthread.h>
   120  
   121  /* We don't want sys/mman.h to be included after we redefine mmap and
   122     dlmunmap.  */
   123  #include <sys/mman.h>
   124  #define LACKS_SYS_MMAN_H 1
   125  
   126  #if FFI_MMAP_EXEC_SELINUX
   127  #include <sys/statfs.h>
   128  #include <stdlib.h>
   129  
   130  static int selinux_enabled = -1;
   131  
   132  static int
   133  selinux_enabled_check (void)
   134  {
   135    struct statfs sfs;
   136    FILE *f;
   137    char *buf = NULL;
   138    size_t len = 0;
   139  
   140    if (statfs ("/selinux", &sfs) >= 0
   141        && (unsigned int) sfs.f_type == 0xf97cff8cU)
   142      return 1;
   143    f = fopen ("/proc/mounts", "r");
   144    if (f == NULL)
   145      return 0;
   146    while (getline (&buf, &len, f) >= 0)
   147      {
   148        char *p = strchr (buf, ' ');
   149        if (p == NULL)
   150          break;
   151        p = strchr (p + 1, ' ');
   152        if (p == NULL)
   153          break;
   154        if (strncmp (p + 1, "selinuxfs ", 10) == 0)
   155          {
   156            free (buf);
   157            fclose (f);
   158            return 1;
   159          }
   160      }
   161    free (buf);
   162    fclose (f);
   163    return 0;
   164  }
   165  
   166  #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
   167  			      : (selinux_enabled = selinux_enabled_check ()))
   168  
   169  #else
   170  
   171  #define is_selinux_enabled() 0
   172  
   173  #endif /* !FFI_MMAP_EXEC_SELINUX */
   174  
   175  /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
   176  #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
   177  #include <stdlib.h>
   178  
   179  static int emutramp_enabled = -1;
   180  
   181  static int
   182  emutramp_enabled_check (void)
   183  {
   184    char *buf = NULL;
   185    size_t len = 0;
   186    FILE *f;
   187    int ret;
   188    f = fopen ("/proc/self/status", "r");
   189    if (f == NULL)
   190      return 0;
   191    ret = 0;
   192  
   193    while (getline (&buf, &len, f) != -1)
   194      if (!strncmp (buf, "PaX:", 4))
   195        {
   196          char emutramp;
   197          if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
   198            ret = (emutramp == 'E');
   199          break;
   200        }
   201    free (buf);
   202    fclose (f);
   203    return ret;
   204  }
   205  
   206  #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
   207                                 : (emutramp_enabled = emutramp_enabled_check ()))
   208  #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
   209  
   210  #elif defined (__CYGWIN__) || defined(__INTERIX)
   211  
   212  #include <sys/mman.h>
   213  
   214  /* Cygwin is Linux-like, but not quite that Linux-like.  */
   215  #define is_selinux_enabled() 0
   216  
   217  #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
   218  
   219  #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
   220  #define is_emutramp_enabled() 0
   221  #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
   222  
   223  /* Declare all functions defined in dlmalloc.c as static.  */
   224  static void *dlmalloc(size_t);
   225  static void dlfree(void*);
   226  static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
   227  static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
   228  static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
   229  static void *dlvalloc(size_t) MAYBE_UNUSED;
   230  static int dlmallopt(int, int) MAYBE_UNUSED;
   231  static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
   232  static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
   233  static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
   234  static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
   235  static void *dlpvalloc(size_t) MAYBE_UNUSED;
   236  static int dlmalloc_trim(size_t) MAYBE_UNUSED;
   237  static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
   238  static void dlmalloc_stats(void) MAYBE_UNUSED;
   239  
   240  #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
   241  /* Use these for mmap and munmap within dlmalloc.c.  */
   242  static void *dlmmap(void *, size_t, int, int, int, off_t);
   243  static int dlmunmap(void *, size_t);
   244  #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
   245  
   246  #define mmap dlmmap
   247  #define munmap dlmunmap
   248  
   249  #include "dlmalloc.c"
   250  
   251  #undef mmap
   252  #undef munmap
   253  
   254  #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
   255  
   256  /* A mutex used to synchronize access to *exec* variables in this file.  */
   257  static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
   258  
   259  /* A file descriptor of a temporary file from which we'll map
   260     executable pages.  */
   261  static int execfd = -1;
   262  
   263  /* The amount of space already allocated from the temporary file.  */
   264  static size_t execsize = 0;
   265  
   266  /* Open a temporary file name, and immediately unlink it.  */
   267  static int
   268  open_temp_exec_file_name (char *name, int flags)
   269  {
   270    int fd;
   271  
   272  #ifdef HAVE_MKOSTEMP
   273    fd = mkostemp (name, flags);
   274  #else
   275    fd = mkstemp (name);
   276  #endif
   277  
   278    if (fd != -1)
   279      unlink (name);
   280  
   281    return fd;
   282  }
   283  
   284  /* Open a temporary file in the named directory.  */
   285  static int
   286  open_temp_exec_file_dir (const char *dir)
   287  {
   288    static const char suffix[] = "/ffiXXXXXX";
   289    int lendir, flags;
   290    char *tempname;
   291  #ifdef O_TMPFILE
   292    int fd;
   293  #endif
   294  
   295  #ifdef O_CLOEXEC
   296    flags = O_CLOEXEC;
   297  #else
   298    flags = 0;
   299  #endif
   300  
   301  #ifdef O_TMPFILE
   302    fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
   303    /* If the running system does not support the O_TMPFILE flag then retry without it. */
   304    if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
   305      return fd;
   306    } else {
   307      errno = 0;
   308    }
   309  #endif
   310  
   311    lendir = strlen (dir);
   312    tempname = __builtin_alloca (lendir + sizeof (suffix));
   313  
   314    if (!tempname)
   315      return -1;
   316  
   317    memcpy (tempname, dir, lendir);
   318    memcpy (tempname + lendir, suffix, sizeof (suffix));
   319  
   320    return open_temp_exec_file_name (tempname, flags);
   321  }
   322  
   323  /* Open a temporary file in the directory in the named environment
   324     variable.  */
   325  static int
   326  open_temp_exec_file_env (const char *envvar)
   327  {
   328    const char *value = getenv (envvar);
   329  
   330    if (!value)
   331      return -1;
   332  
   333    return open_temp_exec_file_dir (value);
   334  }
   335  
   336  #ifdef HAVE_MNTENT
   337  /* Open a temporary file in an executable and writable mount point
   338     listed in the mounts file.  Subsequent calls with the same mounts
   339     keep searching for mount points in the same file.  Providing NULL
   340     as the mounts file closes the file.  */
   341  static int
   342  open_temp_exec_file_mnt (const char *mounts)
   343  {
   344    static const char *last_mounts;
   345    static FILE *last_mntent;
   346  
   347    if (mounts != last_mounts)
   348      {
   349        if (last_mntent)
   350  	endmntent (last_mntent);
   351  
   352        last_mounts = mounts;
   353  
   354        if (mounts)
   355  	last_mntent = setmntent (mounts, "r");
   356        else
   357  	last_mntent = NULL;
   358      }
   359  
   360    if (!last_mntent)
   361      return -1;
   362  
   363    for (;;)
   364      {
   365        int fd;
   366        struct mntent mnt;
   367        char buf[MAXPATHLEN * 3];
   368  
   369        if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
   370  	return -1;
   371  
   372        if (hasmntopt (&mnt, "ro")
   373  	  || hasmntopt (&mnt, "noexec")
   374  	  || access (mnt.mnt_dir, W_OK))
   375  	continue;
   376  
   377        fd = open_temp_exec_file_dir (mnt.mnt_dir);
   378  
   379        if (fd != -1)
   380  	return fd;
   381      }
   382  }
   383  #endif /* HAVE_MNTENT */
   384  
   385  /* Instructions to look for a location to hold a temporary file that
   386     can be mapped in for execution.  */
   387  static struct
   388  {
   389    int (*func)(const char *);
   390    const char *arg;
   391    int repeat;
   392  } open_temp_exec_file_opts[] = {
   393    { open_temp_exec_file_env, "TMPDIR", 0 },
   394    { open_temp_exec_file_dir, "/tmp", 0 },
   395    { open_temp_exec_file_dir, "/var/tmp", 0 },
   396    { open_temp_exec_file_dir, "/dev/shm", 0 },
   397    { open_temp_exec_file_env, "HOME", 0 },
   398  #ifdef HAVE_MNTENT
   399    { open_temp_exec_file_mnt, "/etc/mtab", 1 },
   400    { open_temp_exec_file_mnt, "/proc/mounts", 1 },
   401  #endif /* HAVE_MNTENT */
   402  };
   403  
   404  /* Current index into open_temp_exec_file_opts.  */
   405  static int open_temp_exec_file_opts_idx = 0;
   406  
   407  /* Reset a current multi-call func, then advances to the next entry.
   408     If we're at the last, go back to the first and return nonzero,
   409     otherwise return zero.  */
   410  static int
   411  open_temp_exec_file_opts_next (void)
   412  {
   413    if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
   414      open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
   415  
   416    open_temp_exec_file_opts_idx++;
   417    if (open_temp_exec_file_opts_idx
   418        == (sizeof (open_temp_exec_file_opts)
   419  	  / sizeof (*open_temp_exec_file_opts)))
   420      {
   421        open_temp_exec_file_opts_idx = 0;
   422        return 1;
   423      }
   424  
   425    return 0;
   426  }
   427  
   428  /* Return a file descriptor of a temporary zero-sized file in a
   429     writable and executable filesystem.  */
   430  static int
   431  open_temp_exec_file (void)
   432  {
   433    int fd;
   434  
   435    do
   436      {
   437        fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
   438  	(open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
   439  
   440        if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
   441  	  || fd == -1)
   442  	{
   443  	  if (open_temp_exec_file_opts_next ())
   444  	    break;
   445  	}
   446      }
   447    while (fd == -1);
   448  
   449    return fd;
   450  }
   451  
   452  /* Map in a chunk of memory from the temporary exec file into separate
   453     locations in the virtual memory address space, one writable and one
   454     executable.  Returns the address of the writable portion, after
   455     storing an offset to the corresponding executable portion at the
   456     last word of the requested chunk.  */
   457  static void *
   458  dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
   459  {
   460    void *ptr;
   461  
   462    if (execfd == -1)
   463      {
   464        open_temp_exec_file_opts_idx = 0;
   465      retry_open:
   466        execfd = open_temp_exec_file ();
   467        if (execfd == -1)
   468  	return MFAIL;
   469      }
   470  
   471    offset = execsize;
   472  
   473    if (ftruncate (execfd, offset + length))
   474      return MFAIL;
   475  
   476    flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
   477    flags |= MAP_SHARED;
   478  
   479    ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
   480  	      flags, execfd, offset);
   481    if (ptr == MFAIL)
   482      {
   483        if (!offset)
   484  	{
   485  	  close (execfd);
   486  	  goto retry_open;
   487  	}
   488        ftruncate (execfd, offset);
   489        return MFAIL;
   490      }
   491    else if (!offset
   492  	   && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
   493      open_temp_exec_file_opts_next ();
   494  
   495    start = mmap (start, length, prot, flags, execfd, offset);
   496  
   497    if (start == MFAIL)
   498      {
   499        munmap (ptr, length);
   500        ftruncate (execfd, offset);
   501        return start;
   502      }
   503  
   504    mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
   505  
   506    execsize += length;
   507  
   508    return start;
   509  }
   510  
   511  /* Map in a writable and executable chunk of memory if possible.
   512     Failing that, fall back to dlmmap_locked.  */
   513  static void *
   514  dlmmap (void *start, size_t length, int prot,
   515  	int flags, int fd, off_t offset)
   516  {
   517    void *ptr;
   518  
   519    assert (start == NULL && length % malloc_getpagesize == 0
   520  	  && prot == (PROT_READ | PROT_WRITE)
   521  	  && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
   522  	  && fd == -1 && offset == 0);
   523  
   524  #if FFI_CLOSURE_TEST
   525    printf ("mapping in %zi\n", length);
   526  #endif
   527  
   528    if (execfd == -1 && is_emutramp_enabled ())
   529      {
   530        ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
   531        return ptr;
   532      }
   533  
   534    if (execfd == -1 && !is_selinux_enabled ())
   535      {
   536        ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
   537  
   538        if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
   539  	/* Cool, no need to mess with separate segments.  */
   540  	return ptr;
   541  
   542        /* If MREMAP_DUP is ever introduced and implemented, try mmap
   543  	 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
   544  	 MREMAP_DUP and prot at this point.  */
   545      }
   546  
   547    if (execsize == 0 || execfd == -1)
   548      {
   549        pthread_mutex_lock (&open_temp_exec_file_mutex);
   550        ptr = dlmmap_locked (start, length, prot, flags, offset);
   551        pthread_mutex_unlock (&open_temp_exec_file_mutex);
   552  
   553        return ptr;
   554      }
   555  
   556    return dlmmap_locked (start, length, prot, flags, offset);
   557  }
   558  
   559  /* Release memory at the given address, as well as the corresponding
   560     executable page if it's separate.  */
   561  static int
   562  dlmunmap (void *start, size_t length)
   563  {
   564    /* We don't bother decreasing execsize or truncating the file, since
   565       we can't quite tell whether we're unmapping the end of the file.
   566       We don't expect frequent deallocation anyway.  If we did, we
   567       could locate pages in the file by writing to the pages being
   568       deallocated and checking that the file contents change.
   569       Yuck.  */
   570    msegmentptr seg = segment_holding (gm, start);
   571    void *code;
   572  
   573  #if FFI_CLOSURE_TEST
   574    printf ("unmapping %zi\n", length);
   575  #endif
   576  
   577    if (seg && (code = add_segment_exec_offset (start, seg)) != start)
   578      {
   579        int ret = munmap (code, length);
   580        if (ret)
   581  	return ret;
   582      }
   583  
   584    return munmap (start, length);
   585  }
   586  
   587  #if FFI_CLOSURE_FREE_CODE
   588  /* Return segment holding given code address.  */
   589  static msegmentptr
   590  segment_holding_code (mstate m, char* addr)
   591  {
   592    msegmentptr sp = &m->seg;
   593    for (;;) {
   594      if (addr >= add_segment_exec_offset (sp->base, sp)
   595  	&& addr < add_segment_exec_offset (sp->base, sp) + sp->size)
   596        return sp;
   597      if ((sp = sp->next) == 0)
   598        return 0;
   599    }
   600  }
   601  #endif
   602  
   603  #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
   604  
   605  /* Allocate a chunk of memory with the given size.  Returns a pointer
   606     to the writable address, and sets *CODE to the executable
   607     corresponding virtual address.  */
   608  void *
   609  ffi_closure_alloc (size_t size, void **code)
   610  {
   611    void *ptr;
   612  
   613    if (!code)
   614      return NULL;
   615  
   616    ptr = dlmalloc (size);
   617  
   618    if (ptr)
   619      {
   620        msegmentptr seg = segment_holding (gm, ptr);
   621  
   622        *code = add_segment_exec_offset (ptr, seg);
   623      }
   624  
   625    return ptr;
   626  }
   627  
   628  /* Release a chunk of memory allocated with ffi_closure_alloc.  If
   629     FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
   630     writable or the executable address given.  Otherwise, only the
   631     writable address can be provided here.  */
   632  void
   633  ffi_closure_free (void *ptr)
   634  {
   635  #if FFI_CLOSURE_FREE_CODE
   636    msegmentptr seg = segment_holding_code (gm, ptr);
   637  
   638    if (seg)
   639      ptr = sub_segment_exec_offset (ptr, seg);
   640  #endif
   641  
   642    dlfree (ptr);
   643  }
   644  
   645  
   646  #if FFI_CLOSURE_TEST
   647  /* Do some internal sanity testing to make sure allocation and
   648     deallocation of pages are working as intended.  */
   649  int main ()
   650  {
   651    void *p[3];
   652  #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
   653  #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
   654    GET (0, malloc_getpagesize / 2);
   655    GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
   656    PUT (1);
   657    GET (1, 2 * malloc_getpagesize);
   658    GET (2, malloc_getpagesize / 2);
   659    PUT (1);
   660    PUT (0);
   661    PUT (2);
   662    return 0;
   663  }
   664  #endif /* FFI_CLOSURE_TEST */
   665  # else /* ! FFI_MMAP_EXEC_WRIT */
   666  
   667  /* On many systems, memory returned by malloc is writable and
   668     executable, so just use it.  */
   669  
   670  #include <stdlib.h>
   671  
   672  void *
   673  ffi_closure_alloc (size_t size, void **code)
   674  {
   675    if (!code)
   676      return NULL;
   677  
   678    return *code = malloc (size);
   679  }
   680  
   681  void
   682  ffi_closure_free (void *ptr)
   683  {
   684    free (ptr);
   685  }
   686  
   687  # endif /* ! FFI_MMAP_EXEC_WRIT */
   688  #endif /* FFI_CLOSURES */