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 #ifdef GSTREAMER_LITE
 622       if (seg == NULL)
 623         return NULL;
 624 #endif // GSTREAMER_LITE
 625 
 626       *code = add_segment_exec_offset (ptr, seg);
 627     }
 628 
 629   return ptr;
 630 }
 631 
 632 /* Release a chunk of memory allocated with ffi_closure_alloc.  If
 633    FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
 634    writable or the executable address given.  Otherwise, only the
 635    writable address can be provided here.  */
 636 void
 637 ffi_closure_free (void *ptr)
 638 {
 639 #if FFI_CLOSURE_FREE_CODE
 640   msegmentptr seg = segment_holding_code (gm, ptr);
 641 
 642   if (seg)
 643     ptr = sub_segment_exec_offset (ptr, seg);
 644 #endif
 645 
 646   dlfree (ptr);
 647 }
 648 
 649 
 650 #if FFI_CLOSURE_TEST
 651 /* Do some internal sanity testing to make sure allocation and
 652    deallocation of pages are working as intended.  */
 653 int main ()
 654 {
 655   void *p[3];
 656 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
 657 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
 658   GET (0, malloc_getpagesize / 2);
 659   GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
 660   PUT (1);
 661   GET (1, 2 * malloc_getpagesize);
 662   GET (2, malloc_getpagesize / 2);
 663   PUT (1);
 664   PUT (0);
 665   PUT (2);
 666   return 0;
 667 }
 668 #endif /* FFI_CLOSURE_TEST */
 669 # else /* ! FFI_MMAP_EXEC_WRIT */
 670 
 671 /* On many systems, memory returned by malloc is writable and
 672    executable, so just use it.  */
 673 
 674 #include <stdlib.h>
 675 
 676 void *
 677 ffi_closure_alloc (size_t size, void **code)
 678 {
 679   if (!code)
 680     return NULL;
 681 
 682   return *code = malloc (size);
 683 }
 684 
 685 void
 686 ffi_closure_free (void *ptr)
 687 {
 688   free (ptr);
 689 }
 690 
 691 # endif /* ! FFI_MMAP_EXEC_WRIT */
 692 #endif /* FFI_CLOSURES */