2009-07-17 Richard Guenther <rguenther@suse.de>
[official-gcc.git] / libffi / src / closures.c
blob7692c857162630b0c4abe7e021c2ee163ad41477
1 /* -----------------------------------------------------------------------
2 closures.c - Copyright (c) 2007 Red Hat, Inc.
3 Copyright (C) 2007 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)
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
169 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
171 /* Declare all functions defined in dlmalloc.c as static. */
172 static void *dlmalloc(size_t);
173 static void dlfree(void*);
174 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
175 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
176 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
177 static void *dlvalloc(size_t) MAYBE_UNUSED;
178 static int dlmallopt(int, int) MAYBE_UNUSED;
179 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
180 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
181 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
182 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
183 static void *dlpvalloc(size_t) MAYBE_UNUSED;
184 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
185 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
186 static void dlmalloc_stats(void) MAYBE_UNUSED;
188 #if !defined(X86_WIN32) && !defined(X86_WIN64)
189 /* Use these for mmap and munmap within dlmalloc.c. */
190 static void *dlmmap(void *, size_t, int, int, int, off_t);
191 static int dlmunmap(void *, size_t);
192 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
194 #define mmap dlmmap
195 #define munmap dlmunmap
197 #include "dlmalloc.c"
199 #undef mmap
200 #undef munmap
202 #if !defined(X86_WIN32) && !defined(X86_WIN64)
204 /* A mutex used to synchronize access to *exec* variables in this file. */
205 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
207 /* A file descriptor of a temporary file from which we'll map
208 executable pages. */
209 static int execfd = -1;
211 /* The amount of space already allocated from the temporary file. */
212 static size_t execsize = 0;
214 /* Open a temporary file name, and immediately unlink it. */
215 static int
216 open_temp_exec_file_name (char *name)
218 int fd = mkstemp (name);
220 if (fd != -1)
221 unlink (name);
223 return fd;
226 /* Open a temporary file in the named directory. */
227 static int
228 open_temp_exec_file_dir (const char *dir)
230 static const char suffix[] = "/ffiXXXXXX";
231 int lendir = strlen (dir);
232 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
234 if (!tempname)
235 return -1;
237 memcpy (tempname, dir, lendir);
238 memcpy (tempname + lendir, suffix, sizeof (suffix));
240 return open_temp_exec_file_name (tempname);
243 /* Open a temporary file in the directory in the named environment
244 variable. */
245 static int
246 open_temp_exec_file_env (const char *envvar)
248 const char *value = getenv (envvar);
250 if (!value)
251 return -1;
253 return open_temp_exec_file_dir (value);
256 #ifdef HAVE_MNTENT
257 /* Open a temporary file in an executable and writable mount point
258 listed in the mounts file. Subsequent calls with the same mounts
259 keep searching for mount points in the same file. Providing NULL
260 as the mounts file closes the file. */
261 static int
262 open_temp_exec_file_mnt (const char *mounts)
264 static const char *last_mounts;
265 static FILE *last_mntent;
267 if (mounts != last_mounts)
269 if (last_mntent)
270 endmntent (last_mntent);
272 last_mounts = mounts;
274 if (mounts)
275 last_mntent = setmntent (mounts, "r");
276 else
277 last_mntent = NULL;
280 if (!last_mntent)
281 return -1;
283 for (;;)
285 int fd;
286 struct mntent mnt;
287 char buf[MAXPATHLEN * 3];
289 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
290 return -1;
292 if (hasmntopt (&mnt, "ro")
293 || hasmntopt (&mnt, "noexec")
294 || access (mnt.mnt_dir, W_OK))
295 continue;
297 fd = open_temp_exec_file_dir (mnt.mnt_dir);
299 if (fd != -1)
300 return fd;
303 #endif /* HAVE_MNTENT */
305 /* Instructions to look for a location to hold a temporary file that
306 can be mapped in for execution. */
307 static struct
309 int (*func)(const char *);
310 const char *arg;
311 int repeat;
312 } open_temp_exec_file_opts[] = {
313 { open_temp_exec_file_env, "TMPDIR", 0 },
314 { open_temp_exec_file_dir, "/tmp", 0 },
315 { open_temp_exec_file_dir, "/var/tmp", 0 },
316 { open_temp_exec_file_dir, "/dev/shm", 0 },
317 { open_temp_exec_file_env, "HOME", 0 },
318 #ifdef HAVE_MNTENT
319 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
320 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
321 #endif /* HAVE_MNTENT */
324 /* Current index into open_temp_exec_file_opts. */
325 static int open_temp_exec_file_opts_idx = 0;
327 /* Reset a current multi-call func, then advances to the next entry.
328 If we're at the last, go back to the first and return nonzero,
329 otherwise return zero. */
330 static int
331 open_temp_exec_file_opts_next (void)
333 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
334 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
336 open_temp_exec_file_opts_idx++;
337 if (open_temp_exec_file_opts_idx
338 == (sizeof (open_temp_exec_file_opts)
339 / sizeof (*open_temp_exec_file_opts)))
341 open_temp_exec_file_opts_idx = 0;
342 return 1;
345 return 0;
348 /* Return a file descriptor of a temporary zero-sized file in a
349 writable and exexutable filesystem. */
350 static int
351 open_temp_exec_file (void)
353 int fd;
357 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
358 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
360 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
361 || fd == -1)
363 if (open_temp_exec_file_opts_next ())
364 break;
367 while (fd == -1);
369 return fd;
372 /* Map in a chunk of memory from the temporary exec file into separate
373 locations in the virtual memory address space, one writable and one
374 executable. Returns the address of the writable portion, after
375 storing an offset to the corresponding executable portion at the
376 last word of the requested chunk. */
377 static void *
378 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
380 void *ptr;
382 if (execfd == -1)
384 open_temp_exec_file_opts_idx = 0;
385 retry_open:
386 execfd = open_temp_exec_file ();
387 if (execfd == -1)
388 return MFAIL;
391 offset = execsize;
393 if (ftruncate (execfd, offset + length))
394 return MFAIL;
396 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
397 flags |= MAP_SHARED;
399 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
400 flags, execfd, offset);
401 if (ptr == MFAIL)
403 if (!offset)
405 close (execfd);
406 goto retry_open;
408 ftruncate (execfd, offset);
409 return MFAIL;
411 else if (!offset
412 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
413 open_temp_exec_file_opts_next ();
415 start = mmap (start, length, prot, flags, execfd, offset);
417 if (start == MFAIL)
419 munmap (ptr, length);
420 ftruncate (execfd, offset);
421 return start;
424 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
426 execsize += length;
428 return start;
431 /* Map in a writable and executable chunk of memory if possible.
432 Failing that, fall back to dlmmap_locked. */
433 static void *
434 dlmmap (void *start, size_t length, int prot,
435 int flags, int fd, off_t offset)
437 void *ptr;
439 assert (start == NULL && length % malloc_getpagesize == 0
440 && prot == (PROT_READ | PROT_WRITE)
441 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
442 && fd == -1 && offset == 0);
444 #if FFI_CLOSURE_TEST
445 printf ("mapping in %zi\n", length);
446 #endif
448 if (execfd == -1 && !is_selinux_enabled ())
450 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
452 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
453 /* Cool, no need to mess with separate segments. */
454 return ptr;
456 /* If MREMAP_DUP is ever introduced and implemented, try mmap
457 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
458 MREMAP_DUP and prot at this point. */
461 if (execsize == 0 || execfd == -1)
463 pthread_mutex_lock (&open_temp_exec_file_mutex);
464 ptr = dlmmap_locked (start, length, prot, flags, offset);
465 pthread_mutex_unlock (&open_temp_exec_file_mutex);
467 return ptr;
470 return dlmmap_locked (start, length, prot, flags, offset);
473 /* Release memory at the given address, as well as the corresponding
474 executable page if it's separate. */
475 static int
476 dlmunmap (void *start, size_t length)
478 /* We don't bother decreasing execsize or truncating the file, since
479 we can't quite tell whether we're unmapping the end of the file.
480 We don't expect frequent deallocation anyway. If we did, we
481 could locate pages in the file by writing to the pages being
482 deallocated and checking that the file contents change.
483 Yuck. */
484 msegmentptr seg = segment_holding (gm, start);
485 void *code;
487 #if FFI_CLOSURE_TEST
488 printf ("unmapping %zi\n", length);
489 #endif
491 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
493 int ret = munmap (code, length);
494 if (ret)
495 return ret;
498 return munmap (start, length);
501 #if FFI_CLOSURE_FREE_CODE
502 /* Return segment holding given code address. */
503 static msegmentptr
504 segment_holding_code (mstate m, char* addr)
506 msegmentptr sp = &m->seg;
507 for (;;) {
508 if (addr >= add_segment_exec_offset (sp->base, sp)
509 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
510 return sp;
511 if ((sp = sp->next) == 0)
512 return 0;
515 #endif
517 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
519 /* Allocate a chunk of memory with the given size. Returns a pointer
520 to the writable address, and sets *CODE to the executable
521 corresponding virtual address. */
522 void *
523 ffi_closure_alloc (size_t size, void **code)
525 void *ptr;
527 if (!code)
528 return NULL;
530 ptr = dlmalloc (size);
532 if (ptr)
534 msegmentptr seg = segment_holding (gm, ptr);
536 *code = add_segment_exec_offset (ptr, seg);
539 return ptr;
542 /* Release a chunk of memory allocated with ffi_closure_alloc. If
543 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
544 writable or the executable address given. Otherwise, only the
545 writable address can be provided here. */
546 void
547 ffi_closure_free (void *ptr)
549 #if FFI_CLOSURE_FREE_CODE
550 msegmentptr seg = segment_holding_code (gm, ptr);
552 if (seg)
553 ptr = sub_segment_exec_offset (ptr, seg);
554 #endif
556 dlfree (ptr);
560 #if FFI_CLOSURE_TEST
561 /* Do some internal sanity testing to make sure allocation and
562 deallocation of pages are working as intended. */
563 int main ()
565 void *p[3];
566 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
567 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
568 GET (0, malloc_getpagesize / 2);
569 GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
570 PUT (1);
571 GET (1, 2 * malloc_getpagesize);
572 GET (2, malloc_getpagesize / 2);
573 PUT (1);
574 PUT (0);
575 PUT (2);
576 return 0;
578 #endif /* FFI_CLOSURE_TEST */
579 # else /* ! FFI_MMAP_EXEC_WRIT */
581 /* On many systems, memory returned by malloc is writable and
582 executable, so just use it. */
584 #include <stdlib.h>
586 void *
587 ffi_closure_alloc (size_t size, void **code)
589 if (!code)
590 return NULL;
592 return *code = malloc (size);
595 void
596 ffi_closure_free (void *ptr)
598 free (ptr);
601 # endif /* ! FFI_MMAP_EXEC_WRIT */
602 #endif /* FFI_CLOSURES */