PageRenderTime 40ms CodeModel.GetById 12ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/js/src/ctypes/libffi/src/closures.c

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