2012-12-21 Steve Ellcey <sellcey@mips.com>
[official-gcc.git] / libffi / src / closures.c
blob1b378270363bed1b68e029f8a32247d4e54eecbd
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.
6 Code to allocate and deallocate memory for closures.
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:
16 The above copyright notice and this permission notice shall be included
17 in all copies or substantial portions of the Software.
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 ----------------------------------------------------------------------- */
29 #if defined __linux__ && !defined _GNU_SOURCE
30 #define _GNU_SOURCE 1
31 #endif
33 #include <ffi.h>
34 #include <ffi_common.h>
36 #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
37 # if __gnu_linux__
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
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
65 #if FFI_CLOSURES
67 # if FFI_EXEC_TRAMPOLINE_TABLE
69 // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
71 # elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
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
81 /* We need to use mmap, not sbrk. */
82 #define HAVE_MORECORE 0
84 /* We could, in theory, support mremap, but it wouldn't buy us anything. */
85 #define HAVE_MREMAP 0
87 /* We have no use for this, so save some code and data. */
88 #define NO_MALLINFO 1
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
94 /* Don't allocate more than a page unless needed. */
95 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
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
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>
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
126 #if FFI_MMAP_EXEC_SELINUX
127 #include <sys/statfs.h>
128 #include <stdlib.h>
130 static int selinux_enabled = -1;
132 static int
133 selinux_enabled_check (void)
135 struct statfs sfs;
136 FILE *f;
137 char *buf = NULL;
138 size_t len = 0;
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)
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)
156 free (buf);
157 fclose (f);
158 return 1;
161 free (buf);
162 fclose (f);
163 return 0;
166 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
167 : (selinux_enabled = selinux_enabled_check ()))
169 #else
171 #define is_selinux_enabled() 0
173 #endif /* !FFI_MMAP_EXEC_SELINUX */
175 #elif defined (__CYGWIN__) || defined(__INTERIX)
177 #include <sys/mman.h>
179 /* Cygwin is Linux-like, but not quite that Linux-like. */
180 #define is_selinux_enabled() 0
182 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
184 /* Declare all functions defined in dlmalloc.c as static. */
185 static void *dlmalloc(size_t);
186 static void dlfree(void*);
187 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
188 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
189 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
190 static void *dlvalloc(size_t) MAYBE_UNUSED;
191 static int dlmallopt(int, int) MAYBE_UNUSED;
192 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
193 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
194 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
195 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
196 static void *dlpvalloc(size_t) MAYBE_UNUSED;
197 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
198 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
199 static void dlmalloc_stats(void) MAYBE_UNUSED;
201 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
202 /* Use these for mmap and munmap within dlmalloc.c. */
203 static void *dlmmap(void *, size_t, int, int, int, off_t);
204 static int dlmunmap(void *, size_t);
205 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
207 #define mmap dlmmap
208 #define munmap dlmunmap
210 #include "dlmalloc.c"
212 #undef mmap
213 #undef munmap
215 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
217 /* A mutex used to synchronize access to *exec* variables in this file. */
218 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
220 /* A file descriptor of a temporary file from which we'll map
221 executable pages. */
222 static int execfd = -1;
224 /* The amount of space already allocated from the temporary file. */
225 static size_t execsize = 0;
227 /* Open a temporary file name, and immediately unlink it. */
228 static int
229 open_temp_exec_file_name (char *name)
231 int fd = mkstemp (name);
233 if (fd != -1)
234 unlink (name);
236 return fd;
239 /* Open a temporary file in the named directory. */
240 static int
241 open_temp_exec_file_dir (const char *dir)
243 static const char suffix[] = "/ffiXXXXXX";
244 int lendir = strlen (dir);
245 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
247 if (!tempname)
248 return -1;
250 memcpy (tempname, dir, lendir);
251 memcpy (tempname + lendir, suffix, sizeof (suffix));
253 return open_temp_exec_file_name (tempname);
256 /* Open a temporary file in the directory in the named environment
257 variable. */
258 static int
259 open_temp_exec_file_env (const char *envvar)
261 const char *value = getenv (envvar);
263 if (!value)
264 return -1;
266 return open_temp_exec_file_dir (value);
269 #ifdef HAVE_MNTENT
270 /* Open a temporary file in an executable and writable mount point
271 listed in the mounts file. Subsequent calls with the same mounts
272 keep searching for mount points in the same file. Providing NULL
273 as the mounts file closes the file. */
274 static int
275 open_temp_exec_file_mnt (const char *mounts)
277 static const char *last_mounts;
278 static FILE *last_mntent;
280 if (mounts != last_mounts)
282 if (last_mntent)
283 endmntent (last_mntent);
285 last_mounts = mounts;
287 if (mounts)
288 last_mntent = setmntent (mounts, "r");
289 else
290 last_mntent = NULL;
293 if (!last_mntent)
294 return -1;
296 for (;;)
298 int fd;
299 struct mntent mnt;
300 char buf[MAXPATHLEN * 3];
302 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
303 return -1;
305 if (hasmntopt (&mnt, "ro")
306 || hasmntopt (&mnt, "noexec")
307 || access (mnt.mnt_dir, W_OK))
308 continue;
310 fd = open_temp_exec_file_dir (mnt.mnt_dir);
312 if (fd != -1)
313 return fd;
316 #endif /* HAVE_MNTENT */
318 /* Instructions to look for a location to hold a temporary file that
319 can be mapped in for execution. */
320 static struct
322 int (*func)(const char *);
323 const char *arg;
324 int repeat;
325 } open_temp_exec_file_opts[] = {
326 { open_temp_exec_file_env, "TMPDIR", 0 },
327 { open_temp_exec_file_dir, "/tmp", 0 },
328 { open_temp_exec_file_dir, "/var/tmp", 0 },
329 { open_temp_exec_file_dir, "/dev/shm", 0 },
330 { open_temp_exec_file_env, "HOME", 0 },
331 #ifdef HAVE_MNTENT
332 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
333 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
334 #endif /* HAVE_MNTENT */
337 /* Current index into open_temp_exec_file_opts. */
338 static int open_temp_exec_file_opts_idx = 0;
340 /* Reset a current multi-call func, then advances to the next entry.
341 If we're at the last, go back to the first and return nonzero,
342 otherwise return zero. */
343 static int
344 open_temp_exec_file_opts_next (void)
346 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
347 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
349 open_temp_exec_file_opts_idx++;
350 if (open_temp_exec_file_opts_idx
351 == (sizeof (open_temp_exec_file_opts)
352 / sizeof (*open_temp_exec_file_opts)))
354 open_temp_exec_file_opts_idx = 0;
355 return 1;
358 return 0;
361 /* Return a file descriptor of a temporary zero-sized file in a
362 writable and exexutable filesystem. */
363 static int
364 open_temp_exec_file (void)
366 int fd;
370 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
371 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
373 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
374 || fd == -1)
376 if (open_temp_exec_file_opts_next ())
377 break;
380 while (fd == -1);
382 return fd;
385 /* Map in a chunk of memory from the temporary exec file into separate
386 locations in the virtual memory address space, one writable and one
387 executable. Returns the address of the writable portion, after
388 storing an offset to the corresponding executable portion at the
389 last word of the requested chunk. */
390 static void *
391 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
393 void *ptr;
395 if (execfd == -1)
397 open_temp_exec_file_opts_idx = 0;
398 retry_open:
399 execfd = open_temp_exec_file ();
400 if (execfd == -1)
401 return MFAIL;
404 offset = execsize;
406 if (ftruncate (execfd, offset + length))
407 return MFAIL;
409 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
410 flags |= MAP_SHARED;
412 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
413 flags, execfd, offset);
414 if (ptr == MFAIL)
416 if (!offset)
418 close (execfd);
419 goto retry_open;
421 ftruncate (execfd, offset);
422 return MFAIL;
424 else if (!offset
425 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
426 open_temp_exec_file_opts_next ();
428 start = mmap (start, length, prot, flags, execfd, offset);
430 if (start == MFAIL)
432 munmap (ptr, length);
433 ftruncate (execfd, offset);
434 return start;
437 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
439 execsize += length;
441 return start;
444 /* Map in a writable and executable chunk of memory if possible.
445 Failing that, fall back to dlmmap_locked. */
446 static void *
447 dlmmap (void *start, size_t length, int prot,
448 int flags, int fd, off_t offset)
450 void *ptr;
452 assert (start == NULL && length % malloc_getpagesize == 0
453 && prot == (PROT_READ | PROT_WRITE)
454 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
455 && fd == -1 && offset == 0);
457 #if FFI_CLOSURE_TEST
458 printf ("mapping in %zi\n", length);
459 #endif
461 if (execfd == -1 && !is_selinux_enabled ())
463 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
465 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
466 /* Cool, no need to mess with separate segments. */
467 return ptr;
469 /* If MREMAP_DUP is ever introduced and implemented, try mmap
470 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
471 MREMAP_DUP and prot at this point. */
474 if (execsize == 0 || execfd == -1)
476 pthread_mutex_lock (&open_temp_exec_file_mutex);
477 ptr = dlmmap_locked (start, length, prot, flags, offset);
478 pthread_mutex_unlock (&open_temp_exec_file_mutex);
480 return ptr;
483 return dlmmap_locked (start, length, prot, flags, offset);
486 /* Release memory at the given address, as well as the corresponding
487 executable page if it's separate. */
488 static int
489 dlmunmap (void *start, size_t length)
491 /* We don't bother decreasing execsize or truncating the file, since
492 we can't quite tell whether we're unmapping the end of the file.
493 We don't expect frequent deallocation anyway. If we did, we
494 could locate pages in the file by writing to the pages being
495 deallocated and checking that the file contents change.
496 Yuck. */
497 msegmentptr seg = segment_holding (gm, start);
498 void *code;
500 #if FFI_CLOSURE_TEST
501 printf ("unmapping %zi\n", length);
502 #endif
504 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
506 int ret = munmap (code, length);
507 if (ret)
508 return ret;
511 return munmap (start, length);
514 #if FFI_CLOSURE_FREE_CODE
515 /* Return segment holding given code address. */
516 static msegmentptr
517 segment_holding_code (mstate m, char* addr)
519 msegmentptr sp = &m->seg;
520 for (;;) {
521 if (addr >= add_segment_exec_offset (sp->base, sp)
522 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
523 return sp;
524 if ((sp = sp->next) == 0)
525 return 0;
528 #endif
530 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
532 /* Allocate a chunk of memory with the given size. Returns a pointer
533 to the writable address, and sets *CODE to the executable
534 corresponding virtual address. */
535 void *
536 ffi_closure_alloc (size_t size, void **code)
538 void *ptr;
540 if (!code)
541 return NULL;
543 ptr = dlmalloc (size);
545 if (ptr)
547 msegmentptr seg = segment_holding (gm, ptr);
549 *code = add_segment_exec_offset (ptr, seg);
552 return ptr;
555 /* Release a chunk of memory allocated with ffi_closure_alloc. If
556 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
557 writable or the executable address given. Otherwise, only the
558 writable address can be provided here. */
559 void
560 ffi_closure_free (void *ptr)
562 #if FFI_CLOSURE_FREE_CODE
563 msegmentptr seg = segment_holding_code (gm, ptr);
565 if (seg)
566 ptr = sub_segment_exec_offset (ptr, seg);
567 #endif
569 dlfree (ptr);
573 #if FFI_CLOSURE_TEST
574 /* Do some internal sanity testing to make sure allocation and
575 deallocation of pages are working as intended. */
576 int main ()
578 void *p[3];
579 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
580 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
581 GET (0, malloc_getpagesize / 2);
582 GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
583 PUT (1);
584 GET (1, 2 * malloc_getpagesize);
585 GET (2, malloc_getpagesize / 2);
586 PUT (1);
587 PUT (0);
588 PUT (2);
589 return 0;
591 #endif /* FFI_CLOSURE_TEST */
592 # else /* ! FFI_MMAP_EXEC_WRIT */
594 /* On many systems, memory returned by malloc is writable and
595 executable, so just use it. */
597 #include <stdlib.h>
599 void *
600 ffi_closure_alloc (size_t size, void **code)
602 if (!code)
603 return NULL;
605 return *code = malloc (size);
608 void
609 ffi_closure_free (void *ptr)
611 free (ptr);
614 # endif /* ! FFI_MMAP_EXEC_WRIT */
615 #endif /* FFI_CLOSURES */