Back to index

plt-scheme  4.2.1
closures.c
Go to the documentation of this file.
00001 /* -----------------------------------------------------------------------
00002    closures.c - Copyright (c) 2007  Red Hat, Inc.
00003    Copyright (C) 2007 Free Software Foundation, Inc
00004 
00005    Code to allocate and deallocate memory for closures.
00006 
00007    Permission is hereby granted, free of charge, to any person obtaining
00008    a copy of this software and associated documentation files (the
00009    ``Software''), to deal in the Software without restriction, including
00010    without limitation the rights to use, copy, modify, merge, publish,
00011    distribute, sublicense, and/or sell copies of the Software, and to
00012    permit persons to whom the Software is furnished to do so, subject to
00013    the following conditions:
00014 
00015    The above copyright notice and this permission notice shall be included
00016    in all copies or substantial portions of the Software.
00017 
00018    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
00019    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00020    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00021    IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
00022    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00023    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00024    OTHER DEALINGS IN THE SOFTWARE.
00025    ----------------------------------------------------------------------- */
00026 
00027 #if defined __linux__ && !defined _GNU_SOURCE
00028 #define _GNU_SOURCE 1
00029 #endif
00030 
00031 #include <ffi.h>
00032 #include <ffi_common.h>
00033 
00034 #ifndef FFI_MMAP_EXEC_WRIT
00035 # if __gnu_linux__
00036 /* This macro indicates it may be forbidden to map anonymous memory
00037    with both write and execute permission.  Code compiled when this
00038    option is defined will attempt to map such pages once, but if it
00039    fails, it falls back to creating a temporary file in a writable and
00040    executable filesystem and mapping pages from it into separate
00041    locations in the virtual memory space, one location writable and
00042    another executable.  */
00043 #  define FFI_MMAP_EXEC_WRIT 1
00044 # endif
00045 #endif
00046 
00047 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
00048 # ifdef __linux__
00049 /* When defined to 1 check for SELinux and if SELinux is active,
00050    don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
00051    might cause audit messages.  */
00052 #  define FFI_MMAP_EXEC_SELINUX 1
00053 # endif
00054 #endif
00055 
00056 #if FFI_CLOSURES
00057 
00058 # if FFI_MMAP_EXEC_WRIT
00059 
00060 #define USE_LOCKS 1
00061 #define USE_DL_PREFIX 1
00062 #define USE_BUILTIN_FFS 1
00063 
00064 /* We need to use mmap, not sbrk.  */
00065 #define HAVE_MORECORE 0
00066 
00067 /* We could, in theory, support mremap, but it wouldn't buy us anything.  */
00068 #define HAVE_MREMAP 0
00069 
00070 /* We have no use for this, so save some code and data.  */
00071 #define NO_MALLINFO 1
00072 
00073 /* We need all allocations to be in regular segments, otherwise we
00074    lose track of the corresponding code address.  */
00075 #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
00076 
00077 /* Don't allocate more than a page unless needed.  */
00078 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
00079 
00080 #if FFI_CLOSURE_TEST
00081 /* Don't release single pages, to avoid a worst-case scenario of
00082    continuously allocating and releasing single pages, but release
00083    pairs of pages, which should do just as well given that allocations
00084    are likely to be small.  */
00085 #define DEFAULT_TRIM_THRESHOLD ((size_t)malloc_getpagesize)
00086 #endif
00087 
00088 #include <sys/types.h>
00089 #include <sys/stat.h>
00090 #include <fcntl.h>
00091 #include <errno.h>
00092 #include <unistd.h>
00093 #include <string.h>
00094 #include <stdio.h>
00095 #include <mntent.h>
00096 #include <sys/param.h>
00097 #include <pthread.h>
00098 
00099 /* We don't want sys/mman.h to be included after we redefine mmap and
00100    dlmunmap.  */
00101 #include <sys/mman.h>
00102 #define LACKS_SYS_MMAN_H 1
00103 
00104 #if FFI_MMAP_EXEC_SELINUX
00105 #include <sys/statfs.h>
00106 #include <stdlib.h>
00107 
00108 static int selinux_enabled = -1;
00109 
00110 static int
00111 selinux_enabled_check (void)
00112 {
00113   struct statfs sfs;
00114   FILE *f;
00115   char *buf = NULL;
00116   size_t len = 0;
00117 
00118   if (statfs ("/selinux", &sfs) >= 0
00119       && (unsigned int) sfs.f_type == 0xf97cff8cU)
00120     return 1;
00121   f = fopen ("/proc/mounts", "r");
00122   if (f == NULL)
00123     return 0;
00124   while (getline (&buf, &len, f) >= 0)
00125     {
00126       char *p = strchr (buf, ' ');
00127       if (p == NULL)
00128         break;
00129       p = strchr (p + 1, ' ');
00130       if (p == NULL)
00131         break;
00132       if (strncmp (p + 1, "selinuxfs ", 10) != 0)
00133         {
00134           free (buf);
00135           fclose (f);
00136           return 1;
00137         }
00138     }
00139   free (buf);
00140   fclose (f);
00141   return 0;
00142 }
00143 
00144 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
00145                            : (selinux_enabled = selinux_enabled_check ()))
00146 
00147 #else
00148 
00149 #define is_selinux_enabled() 0
00150 
00151 #endif
00152 
00153 /* Declare all functions defined in dlmalloc.c as static.  */
00154 static void *dlmalloc(size_t);
00155 static void dlfree(void*);
00156 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
00157 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
00158 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
00159 static void *dlvalloc(size_t) MAYBE_UNUSED;
00160 static int dlmallopt(int, int) MAYBE_UNUSED;
00161 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
00162 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
00163 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
00164 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
00165 static void *dlpvalloc(size_t) MAYBE_UNUSED;
00166 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
00167 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
00168 static void dlmalloc_stats(void) MAYBE_UNUSED;
00169 
00170 /* Use these for mmap and munmap within dlmalloc.c.  */
00171 static void *dlmmap(void *, size_t, int, int, int, off_t);
00172 static int dlmunmap(void *, size_t);
00173 
00174 #define mmap dlmmap
00175 #define munmap dlmunmap
00176 
00177 #include "dlmalloc.c"
00178 
00179 #undef mmap
00180 #undef munmap
00181 
00182 /* A mutex used to synchronize access to *exec* variables in this file.  */
00183 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
00184 
00185 /* A file descriptor of a temporary file from which we'll map
00186    executable pages.  */
00187 static int execfd = -1;
00188 
00189 /* The amount of space already allocated from the temporary file.  */
00190 static size_t execsize = 0;
00191 
00192 /* Open a temporary file name, and immediately unlink it.  */
00193 static int
00194 open_temp_exec_file_name (char *name)
00195 {
00196   int fd = mkstemp (name);
00197 
00198   if (fd != -1)
00199     unlink (name);
00200 
00201   return fd;
00202 }
00203 
00204 /* Open a temporary file in the named directory.  */
00205 static int
00206 open_temp_exec_file_dir (const char *dir)
00207 {
00208   static const char suffix[] = "/ffiXXXXXX";
00209   int lendir = strlen (dir);
00210   char *tempname = __builtin_alloca (lendir + sizeof (suffix));
00211 
00212   if (!tempname)
00213     return -1;
00214 
00215   memcpy (tempname, dir, lendir);
00216   memcpy (tempname + lendir, suffix, sizeof (suffix));
00217 
00218   return open_temp_exec_file_name (tempname);
00219 }
00220 
00221 /* Open a temporary file in the directory in the named environment
00222    variable.  */
00223 static int
00224 open_temp_exec_file_env (const char *envvar)
00225 {
00226   const char *value = getenv (envvar);
00227 
00228   if (!value)
00229     return -1;
00230 
00231   return open_temp_exec_file_dir (value);
00232 }
00233 
00234 /* Open a temporary file in an executable and writable mount point
00235    listed in the mounts file.  Subsequent calls with the same mounts
00236    keep searching for mount points in the same file.  Providing NULL
00237    as the mounts file closes the file.  */
00238 static int
00239 open_temp_exec_file_mnt (const char *mounts)
00240 {
00241   static const char *last_mounts;
00242   static FILE *last_mntent;
00243 
00244   if (mounts != last_mounts)
00245     {
00246       if (last_mntent)
00247        endmntent (last_mntent);
00248 
00249       last_mounts = mounts;
00250 
00251       if (mounts)
00252        last_mntent = setmntent (mounts, "r");
00253       else
00254        last_mntent = NULL;
00255     }
00256 
00257   if (!last_mntent)
00258     return -1;
00259 
00260   for (;;)
00261     {
00262       int fd;
00263       struct mntent mnt;
00264       char buf[MAXPATHLEN * 3];
00265 
00266       if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
00267        return -1;
00268 
00269       if (hasmntopt (&mnt, "ro")
00270          || hasmntopt (&mnt, "noexec")
00271          || access (mnt.mnt_dir, W_OK))
00272        continue;
00273 
00274       fd = open_temp_exec_file_dir (mnt.mnt_dir);
00275 
00276       if (fd != -1)
00277        return fd;
00278     }
00279 }
00280 
00281 /* Instructions to look for a location to hold a temporary file that
00282    can be mapped in for execution.  */
00283 static struct
00284 {
00285   int (*func)(const char *);
00286   const char *arg;
00287   int repeat;
00288 } open_temp_exec_file_opts[] = {
00289   { open_temp_exec_file_env, "TMPDIR", 0 },
00290   { open_temp_exec_file_dir, "/tmp", 0 },
00291   { open_temp_exec_file_dir, "/var/tmp", 0 },
00292   { open_temp_exec_file_dir, "/dev/shm", 0 },
00293   { open_temp_exec_file_env, "HOME", 0 },
00294   { open_temp_exec_file_mnt, "/etc/mtab", 1 },
00295   { open_temp_exec_file_mnt, "/proc/mounts", 1 },
00296 };
00297 
00298 /* Current index into open_temp_exec_file_opts.  */
00299 static int open_temp_exec_file_opts_idx = 0;
00300 
00301 /* Reset a current multi-call func, then advances to the next entry.
00302    If we're at the last, go back to the first and return nonzero,
00303    otherwise return zero.  */
00304 static int
00305 open_temp_exec_file_opts_next (void)
00306 {
00307   if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
00308     open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
00309 
00310   open_temp_exec_file_opts_idx++;
00311   if (open_temp_exec_file_opts_idx
00312       == (sizeof (open_temp_exec_file_opts)
00313          / sizeof (*open_temp_exec_file_opts)))
00314     {
00315       open_temp_exec_file_opts_idx = 0;
00316       return 1;
00317     }
00318 
00319   return 0;
00320 }
00321 
00322 /* Return a file descriptor of a temporary zero-sized file in a
00323    writable and exexutable filesystem.  */
00324 static int
00325 open_temp_exec_file (void)
00326 {
00327   int fd;
00328 
00329   do
00330     {
00331       fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
00332        (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
00333 
00334       if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
00335          || fd == -1)
00336        {
00337          if (open_temp_exec_file_opts_next ())
00338            break;
00339        }
00340     }
00341   while (fd == -1);
00342 
00343   return fd;
00344 }
00345 
00346 /* Map in a chunk of memory from the temporary exec file into separate
00347    locations in the virtual memory address space, one writable and one
00348    executable.  Returns the address of the writable portion, after
00349    storing an offset to the corresponding executable portion at the
00350    last word of the requested chunk.  */
00351 static void *
00352 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
00353 {
00354   void *ptr;
00355 
00356   if (execfd == -1)
00357     {
00358       open_temp_exec_file_opts_idx = 0;
00359     retry_open:
00360       execfd = open_temp_exec_file ();
00361       if (execfd == -1)
00362        return MFAIL;
00363     }
00364 
00365   offset = execsize;
00366 
00367   if (ftruncate (execfd, offset + length))
00368     return MFAIL;
00369 
00370   flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
00371   flags |= MAP_SHARED;
00372 
00373   ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
00374              flags, execfd, offset);
00375   if (ptr == MFAIL)
00376     {
00377       if (!offset)
00378        {
00379          close (execfd);
00380          goto retry_open;
00381        }
00382       ftruncate (execfd, offset);
00383       return MFAIL;
00384     }
00385   else if (!offset
00386           && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
00387     open_temp_exec_file_opts_next ();
00388 
00389   start = mmap (start, length, prot, flags, execfd, offset);
00390 
00391   if (start == MFAIL)
00392     {
00393       munmap (ptr, length);
00394       ftruncate (execfd, offset);
00395       return start;
00396     }
00397 
00398   mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
00399 
00400   execsize += length;
00401 
00402   return start;
00403 }
00404 
00405 /* Map in a writable and executable chunk of memory if possible.
00406    Failing that, fall back to dlmmap_locked.  */
00407 static void *
00408 dlmmap (void *start, size_t length, int prot,
00409        int flags, int fd, off_t offset)
00410 {
00411   void *ptr;
00412 
00413   assert (start == NULL && length % malloc_getpagesize == 0
00414          && prot == (PROT_READ | PROT_WRITE)
00415          && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
00416          && fd == -1 && offset == 0);
00417 
00418 #if FFI_CLOSURE_TEST
00419   printf ("mapping in %zi\n", length);
00420 #endif
00421 
00422   if (execfd == -1 && !is_selinux_enabled ())
00423     {
00424       ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
00425 
00426       if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
00427        /* Cool, no need to mess with separate segments.  */
00428        return ptr;
00429 
00430       /* If MREMAP_DUP is ever introduced and implemented, try mmap
00431         with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
00432         MREMAP_DUP and prot at this point.  */
00433     }
00434 
00435   if (execsize == 0 || execfd == -1)
00436     {
00437       pthread_mutex_lock (&open_temp_exec_file_mutex);
00438       ptr = dlmmap_locked (start, length, prot, flags, offset);
00439       pthread_mutex_unlock (&open_temp_exec_file_mutex);
00440 
00441       return ptr;
00442     }
00443 
00444   return dlmmap_locked (start, length, prot, flags, offset);
00445 }
00446 
00447 /* Release memory at the given address, as well as the corresponding
00448    executable page if it's separate.  */
00449 static int
00450 dlmunmap (void *start, size_t length)
00451 {
00452   /* We don't bother decreasing execsize or truncating the file, since
00453      we can't quite tell whether we're unmapping the end of the file.
00454      We don't expect frequent deallocation anyway.  If we did, we
00455      could locate pages in the file by writing to the pages being
00456      deallocated and checking that the file contents change.
00457      Yuck.  */
00458   msegmentptr seg = segment_holding (gm, start);
00459   void *code;
00460 
00461 #if FFI_CLOSURE_TEST
00462   printf ("unmapping %zi\n", length);
00463 #endif
00464 
00465   if (seg && (code = add_segment_exec_offset (start, seg)) != start)
00466     {
00467       int ret = munmap (code, length);
00468       if (ret)
00469        return ret;
00470     }
00471 
00472   return munmap (start, length);
00473 }
00474 
00475 #if FFI_CLOSURE_FREE_CODE
00476 /* Return segment holding given code address.  */
00477 static msegmentptr
00478 segment_holding_code (mstate m, char* addr)
00479 {
00480   msegmentptr sp = &m->seg;
00481   for (;;) {
00482     if (addr >= add_segment_exec_offset (sp->base, sp)
00483        && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
00484       return sp;
00485     if ((sp = sp->next) == 0)
00486       return 0;
00487   }
00488 }
00489 #endif
00490 
00491 /* Allocate a chunk of memory with the given size.  Returns a pointer
00492    to the writable address, and sets *CODE to the executable
00493    corresponding virtual address.  */
00494 void *
00495 ffi_closure_alloc (size_t size, void **code)
00496 {
00497   void *ptr;
00498 
00499   if (!code)
00500     return NULL;
00501 
00502   ptr = dlmalloc (size);
00503 
00504   if (ptr)
00505     {
00506       msegmentptr seg = segment_holding (gm, ptr);
00507 
00508       *code = add_segment_exec_offset (ptr, seg);
00509     }
00510 
00511   return ptr;
00512 }
00513 
00514 /* Release a chunk of memory allocated with ffi_closure_alloc.  If
00515    FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
00516    writable or the executable address given.  Otherwise, only the
00517    writable address can be provided here.  */
00518 void
00519 ffi_closure_free (void *ptr)
00520 {
00521 #if FFI_CLOSURE_FREE_CODE
00522   msegmentptr seg = segment_holding_code (gm, ptr);
00523 
00524   if (seg)
00525     ptr = sub_segment_exec_offset (ptr, seg);
00526 #endif
00527 
00528   dlfree (ptr);
00529 }
00530 
00531 
00532 #if FFI_CLOSURE_TEST
00533 /* Do some internal sanity testing to make sure allocation and
00534    deallocation of pages are working as intended.  */
00535 int main ()
00536 {
00537   void *p[3];
00538 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
00539 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
00540   GET (0, malloc_getpagesize / 2);
00541   GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
00542   PUT (1);
00543   GET (1, 2 * malloc_getpagesize);
00544   GET (2, malloc_getpagesize / 2);
00545   PUT (1);
00546   PUT (0);
00547   PUT (2);
00548   return 0;
00549 }
00550 #endif /* FFI_CLOSURE_TEST */
00551 # else /* ! FFI_MMAP_EXEC_WRIT */
00552 
00553 /* On many systems, memory returned by malloc is writable and
00554    executable, so just use it.  */
00555 
00556 #include <stdlib.h>
00557 
00558 void *
00559 ffi_closure_alloc (size_t size, void **code)
00560 {
00561   if (!code)
00562     return NULL;
00563 
00564   return *code = malloc (size);
00565 }
00566 
00567 void
00568 ffi_closure_free (void *ptr)
00569 {
00570   free (ptr);
00571 }
00572 
00573 # endif /* ! FFI_MMAP_EXEC_WRIT */
00574 #endif /* FFI_CLOSURES */