Merge from mainline (163495:164578).
[official-gcc/graphite-test-results.git] / libffi / src / closures.c
blobd7b338b5f5ef9a43c0e7a3a374f43a2f77b5f9d0
1 /* -----------------------------------------------------------------------
2 closures.c - Copyright (c) 2007 Red Hat, Inc.
3 Copyright (C) 2007, 2009 Free Software Foundation, Inc
5 Code to allocate and deallocate memory for closures.
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:
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
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 ----------------------------------------------------------------------- */
28 #if defined __linux__ && !defined _GNU_SOURCE
29 #define _GNU_SOURCE 1
30 #endif
32 #include <ffi.h>
33 #include <ffi_common.h>
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
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
64 #if FFI_CLOSURES
66 # if FFI_MMAP_EXEC_WRIT
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
76 /* We need to use mmap, not sbrk. */
77 #define HAVE_MORECORE 0
79 /* We could, in theory, support mremap, but it wouldn't buy us anything. */
80 #define HAVE_MREMAP 0
82 /* We have no use for this, so save some code and data. */
83 #define NO_MALLINFO 1
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
89 /* Don't allocate more than a page unless needed. */
90 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
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
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>
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
121 #if FFI_MMAP_EXEC_SELINUX
122 #include <sys/statfs.h>
123 #include <stdlib.h>
125 static int selinux_enabled = -1;
127 static int
128 selinux_enabled_check (void)
130 struct statfs sfs;
131 FILE *f;
132 char *buf = NULL;
133 size_t len = 0;
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)
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)
151 free (buf);
152 fclose (f);
153 return 1;
156 free (buf);
157 fclose (f);
158 return 0;
161 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
162 : (selinux_enabled = selinux_enabled_check ()))
164 #else
166 #define is_selinux_enabled() 0
168 #endif /* !FFI_MMAP_EXEC_SELINUX */
170 #elif defined (__CYGWIN__)
172 #include <sys/mman.h>
174 /* Cygwin is Linux-like, but not quite that Linux-like. */
175 #define is_selinux_enabled() 0
177 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
179 /* Declare all functions defined in dlmalloc.c as static. */
180 static void *dlmalloc(size_t);
181 static void dlfree(void*);
182 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
183 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
184 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
185 static void *dlvalloc(size_t) MAYBE_UNUSED;
186 static int dlmallopt(int, int) MAYBE_UNUSED;
187 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
188 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
189 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
190 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
191 static void *dlpvalloc(size_t) MAYBE_UNUSED;
192 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
193 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
194 static void dlmalloc_stats(void) MAYBE_UNUSED;
196 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__)
197 /* Use these for mmap and munmap within dlmalloc.c. */
198 static void *dlmmap(void *, size_t, int, int, int, off_t);
199 static int dlmunmap(void *, size_t);
200 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) */
202 #define mmap dlmmap
203 #define munmap dlmunmap
205 #include "dlmalloc.c"
207 #undef mmap
208 #undef munmap
210 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__)
212 /* A mutex used to synchronize access to *exec* variables in this file. */
213 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
215 /* A file descriptor of a temporary file from which we'll map
216 executable pages. */
217 static int execfd = -1;
219 /* The amount of space already allocated from the temporary file. */
220 static size_t execsize = 0;
222 /* Open a temporary file name, and immediately unlink it. */
223 static int
224 open_temp_exec_file_name (char *name)
226 int fd = mkstemp (name);
228 if (fd != -1)
229 unlink (name);
231 return fd;
234 /* Open a temporary file in the named directory. */
235 static int
236 open_temp_exec_file_dir (const char *dir)
238 static const char suffix[] = "/ffiXXXXXX";
239 int lendir = strlen (dir);
240 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
242 if (!tempname)
243 return -1;
245 memcpy (tempname, dir, lendir);
246 memcpy (tempname + lendir, suffix, sizeof (suffix));
248 return open_temp_exec_file_name (tempname);
251 /* Open a temporary file in the directory in the named environment
252 variable. */
253 static int
254 open_temp_exec_file_env (const char *envvar)
256 const char *value = getenv (envvar);
258 if (!value)
259 return -1;
261 return open_temp_exec_file_dir (value);
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. */
269 static int
270 open_temp_exec_file_mnt (const char *mounts)
272 static const char *last_mounts;
273 static FILE *last_mntent;
275 if (mounts != last_mounts)
277 if (last_mntent)
278 endmntent (last_mntent);
280 last_mounts = mounts;
282 if (mounts)
283 last_mntent = setmntent (mounts, "r");
284 else
285 last_mntent = NULL;
288 if (!last_mntent)
289 return -1;
291 for (;;)
293 int fd;
294 struct mntent mnt;
295 char buf[MAXPATHLEN * 3];
297 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
298 return -1;
300 if (hasmntopt (&mnt, "ro")
301 || hasmntopt (&mnt, "noexec")
302 || access (mnt.mnt_dir, W_OK))
303 continue;
305 fd = open_temp_exec_file_dir (mnt.mnt_dir);
307 if (fd != -1)
308 return fd;
311 #endif /* HAVE_MNTENT */
313 /* Instructions to look for a location to hold a temporary file that
314 can be mapped in for execution. */
315 static struct
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 */
332 /* Current index into open_temp_exec_file_opts. */
333 static int open_temp_exec_file_opts_idx = 0;
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. */
338 static int
339 open_temp_exec_file_opts_next (void)
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);
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)))
349 open_temp_exec_file_opts_idx = 0;
350 return 1;
353 return 0;
356 /* Return a file descriptor of a temporary zero-sized file in a
357 writable and exexutable filesystem. */
358 static int
359 open_temp_exec_file (void)
361 int fd;
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);
368 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
369 || fd == -1)
371 if (open_temp_exec_file_opts_next ())
372 break;
375 while (fd == -1);
377 return fd;
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. */
385 static void *
386 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
388 void *ptr;
390 if (execfd == -1)
392 open_temp_exec_file_opts_idx = 0;
393 retry_open:
394 execfd = open_temp_exec_file ();
395 if (execfd == -1)
396 return MFAIL;
399 offset = execsize;
401 if (ftruncate (execfd, offset + length))
402 return MFAIL;
404 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
405 flags |= MAP_SHARED;
407 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
408 flags, execfd, offset);
409 if (ptr == MFAIL)
411 if (!offset)
413 close (execfd);
414 goto retry_open;
416 ftruncate (execfd, offset);
417 return MFAIL;
419 else if (!offset
420 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
421 open_temp_exec_file_opts_next ();
423 start = mmap (start, length, prot, flags, execfd, offset);
425 if (start == MFAIL)
427 munmap (ptr, length);
428 ftruncate (execfd, offset);
429 return start;
432 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
434 execsize += length;
436 return start;
439 /* Map in a writable and executable chunk of memory if possible.
440 Failing that, fall back to dlmmap_locked. */
441 static void *
442 dlmmap (void *start, size_t length, int prot,
443 int flags, int fd, off_t offset)
445 void *ptr;
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);
452 #if FFI_CLOSURE_TEST
453 printf ("mapping in %zi\n", length);
454 #endif
456 if (execfd == -1 && !is_selinux_enabled ())
458 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
460 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
461 /* Cool, no need to mess with separate segments. */
462 return ptr;
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. */
469 if (execsize == 0 || execfd == -1)
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);
475 return ptr;
478 return dlmmap_locked (start, length, prot, flags, offset);
481 /* Release memory at the given address, as well as the corresponding
482 executable page if it's separate. */
483 static int
484 dlmunmap (void *start, size_t length)
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;
495 #if FFI_CLOSURE_TEST
496 printf ("unmapping %zi\n", length);
497 #endif
499 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
501 int ret = munmap (code, length);
502 if (ret)
503 return ret;
506 return munmap (start, length);
509 #if FFI_CLOSURE_FREE_CODE
510 /* Return segment holding given code address. */
511 static msegmentptr
512 segment_holding_code (mstate m, char* addr)
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;
523 #endif
525 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) */
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. */
530 void *
531 ffi_closure_alloc (size_t size, void **code)
533 void *ptr;
535 if (!code)
536 return NULL;
538 ptr = dlmalloc (size);
540 if (ptr)
542 msegmentptr seg = segment_holding (gm, ptr);
544 *code = add_segment_exec_offset (ptr, seg);
547 return ptr;
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. */
554 void
555 ffi_closure_free (void *ptr)
557 #if FFI_CLOSURE_FREE_CODE
558 msegmentptr seg = segment_holding_code (gm, ptr);
560 if (seg)
561 ptr = sub_segment_exec_offset (ptr, seg);
562 #endif
564 dlfree (ptr);
568 #if FFI_CLOSURE_TEST
569 /* Do some internal sanity testing to make sure allocation and
570 deallocation of pages are working as intended. */
571 int main ()
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;
586 #endif /* FFI_CLOSURE_TEST */
587 # else /* ! FFI_MMAP_EXEC_WRIT */
589 /* On many systems, memory returned by malloc is writable and
590 executable, so just use it. */
592 #include <stdlib.h>
594 void *
595 ffi_closure_alloc (size_t size, void **code)
597 if (!code)
598 return NULL;
600 return *code = malloc (size);
603 void
604 ffi_closure_free (void *ptr)
606 free (ptr);
609 # endif /* ! FFI_MMAP_EXEC_WRIT */
610 #endif /* FFI_CLOSURES */