Merge -r 127928:132243 from trunk
[official-gcc.git] / libffi / src / closures.c
blobc441b870f70bd5a3470f8c7a71b52b2f8d8c622c
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, EXPRESS
19 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 OTHER DEALINGS IN THE SOFTWARE.
25 ----------------------------------------------------------------------- */
27 #if defined __linux__ && !defined _GNU_SOURCE
28 #define _GNU_SOURCE 1
29 #endif
31 #include <ffi.h>
32 #include <ffi_common.h>
34 #ifndef FFI_MMAP_EXEC_WRIT
35 # if __gnu_linux__
36 /* This macro indicates it may be forbidden to map anonymous memory
37 with both write and execute permission. Code compiled when this
38 option is defined will attempt to map such pages once, but if it
39 fails, it falls back to creating a temporary file in a writable and
40 executable filesystem and mapping pages from it into separate
41 locations in the virtual memory space, one location writable and
42 another executable. */
43 # define FFI_MMAP_EXEC_WRIT 1
44 # endif
45 #endif
47 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
48 # ifdef __linux__
49 /* When defined to 1 check for SELinux and if SELinux is active,
50 don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
51 might cause audit messages. */
52 # define FFI_MMAP_EXEC_SELINUX 1
53 # endif
54 #endif
56 #if FFI_CLOSURES
58 # if FFI_MMAP_EXEC_WRIT
60 #define USE_LOCKS 1
61 #define USE_DL_PREFIX 1
62 #define USE_BUILTIN_FFS 1
64 /* We need to use mmap, not sbrk. */
65 #define HAVE_MORECORE 0
67 /* We could, in theory, support mremap, but it wouldn't buy us anything. */
68 #define HAVE_MREMAP 0
70 /* We have no use for this, so save some code and data. */
71 #define NO_MALLINFO 1
73 /* We need all allocations to be in regular segments, otherwise we
74 lose track of the corresponding code address. */
75 #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
77 /* Don't allocate more than a page unless needed. */
78 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
80 #if FFI_CLOSURE_TEST
81 /* Don't release single pages, to avoid a worst-case scenario of
82 continuously allocating and releasing single pages, but release
83 pairs of pages, which should do just as well given that allocations
84 are likely to be small. */
85 #define DEFAULT_TRIM_THRESHOLD ((size_t)malloc_getpagesize)
86 #endif
88 #include <sys/types.h>
89 #include <sys/stat.h>
90 #include <fcntl.h>
91 #include <errno.h>
92 #include <unistd.h>
93 #include <string.h>
94 #include <stdio.h>
95 #include <mntent.h>
96 #include <sys/param.h>
97 #include <pthread.h>
99 /* We don't want sys/mman.h to be included after we redefine mmap and
100 dlmunmap. */
101 #include <sys/mman.h>
102 #define LACKS_SYS_MMAN_H 1
104 #if FFI_MMAP_EXEC_SELINUX
105 #include <sys/statfs.h>
106 #include <stdlib.h>
108 static int selinux_enabled = -1;
110 static int
111 selinux_enabled_check (void)
113 struct statfs sfs;
114 FILE *f;
115 char *buf = NULL;
116 size_t len = 0;
118 if (statfs ("/selinux", &sfs) >= 0
119 && (unsigned int) sfs.f_type == 0xf97cff8cU)
120 return 1;
121 f = fopen ("/proc/mounts", "r");
122 if (f == NULL)
123 return 0;
124 while (getline (&buf, &len, f) >= 0)
126 char *p = strchr (buf, ' ');
127 if (p == NULL)
128 break;
129 p = strchr (p + 1, ' ');
130 if (p == NULL)
131 break;
132 if (strncmp (p + 1, "selinuxfs ", 10) != 0)
134 free (buf);
135 fclose (f);
136 return 1;
139 free (buf);
140 fclose (f);
141 return 0;
144 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
145 : (selinux_enabled = selinux_enabled_check ()))
147 #else
149 #define is_selinux_enabled() 0
151 #endif
153 /* Declare all functions defined in dlmalloc.c as static. */
154 static void *dlmalloc(size_t);
155 static void dlfree(void*);
156 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
157 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
158 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
159 static void *dlvalloc(size_t) MAYBE_UNUSED;
160 static int dlmallopt(int, int) MAYBE_UNUSED;
161 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
162 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
163 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
164 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
165 static void *dlpvalloc(size_t) MAYBE_UNUSED;
166 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
167 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
168 static void dlmalloc_stats(void) MAYBE_UNUSED;
170 /* Use these for mmap and munmap within dlmalloc.c. */
171 static void *dlmmap(void *, size_t, int, int, int, off_t);
172 static int dlmunmap(void *, size_t);
174 #define mmap dlmmap
175 #define munmap dlmunmap
177 #include "dlmalloc.c"
179 #undef mmap
180 #undef munmap
182 /* A mutex used to synchronize access to *exec* variables in this file. */
183 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
185 /* A file descriptor of a temporary file from which we'll map
186 executable pages. */
187 static int execfd = -1;
189 /* The amount of space already allocated from the temporary file. */
190 static size_t execsize = 0;
192 /* Open a temporary file name, and immediately unlink it. */
193 static int
194 open_temp_exec_file_name (char *name)
196 int fd = mkstemp (name);
198 if (fd != -1)
199 unlink (name);
201 return fd;
204 /* Open a temporary file in the named directory. */
205 static int
206 open_temp_exec_file_dir (const char *dir)
208 static const char suffix[] = "/ffiXXXXXX";
209 int lendir = strlen (dir);
210 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
212 if (!tempname)
213 return -1;
215 memcpy (tempname, dir, lendir);
216 memcpy (tempname + lendir, suffix, sizeof (suffix));
218 return open_temp_exec_file_name (tempname);
221 /* Open a temporary file in the directory in the named environment
222 variable. */
223 static int
224 open_temp_exec_file_env (const char *envvar)
226 const char *value = getenv (envvar);
228 if (!value)
229 return -1;
231 return open_temp_exec_file_dir (value);
234 /* Open a temporary file in an executable and writable mount point
235 listed in the mounts file. Subsequent calls with the same mounts
236 keep searching for mount points in the same file. Providing NULL
237 as the mounts file closes the file. */
238 static int
239 open_temp_exec_file_mnt (const char *mounts)
241 static const char *last_mounts;
242 static FILE *last_mntent;
244 if (mounts != last_mounts)
246 if (last_mntent)
247 endmntent (last_mntent);
249 last_mounts = mounts;
251 if (mounts)
252 last_mntent = setmntent (mounts, "r");
253 else
254 last_mntent = NULL;
257 if (!last_mntent)
258 return -1;
260 for (;;)
262 int fd;
263 struct mntent mnt;
264 char buf[MAXPATHLEN * 3];
266 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
267 return -1;
269 if (hasmntopt (&mnt, "ro")
270 || hasmntopt (&mnt, "noexec")
271 || access (mnt.mnt_dir, W_OK))
272 continue;
274 fd = open_temp_exec_file_dir (mnt.mnt_dir);
276 if (fd != -1)
277 return fd;
281 /* Instructions to look for a location to hold a temporary file that
282 can be mapped in for execution. */
283 static struct
285 int (*func)(const char *);
286 const char *arg;
287 int repeat;
288 } open_temp_exec_file_opts[] = {
289 { open_temp_exec_file_env, "TMPDIR", 0 },
290 { open_temp_exec_file_dir, "/tmp", 0 },
291 { open_temp_exec_file_dir, "/var/tmp", 0 },
292 { open_temp_exec_file_dir, "/dev/shm", 0 },
293 { open_temp_exec_file_env, "HOME", 0 },
294 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
295 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
298 /* Current index into open_temp_exec_file_opts. */
299 static int open_temp_exec_file_opts_idx = 0;
301 /* Reset a current multi-call func, then advances to the next entry.
302 If we're at the last, go back to the first and return nonzero,
303 otherwise return zero. */
304 static int
305 open_temp_exec_file_opts_next (void)
307 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
308 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
310 open_temp_exec_file_opts_idx++;
311 if (open_temp_exec_file_opts_idx
312 == (sizeof (open_temp_exec_file_opts)
313 / sizeof (*open_temp_exec_file_opts)))
315 open_temp_exec_file_opts_idx = 0;
316 return 1;
319 return 0;
322 /* Return a file descriptor of a temporary zero-sized file in a
323 writable and exexutable filesystem. */
324 static int
325 open_temp_exec_file (void)
327 int fd;
331 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
332 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
334 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
335 || fd == -1)
337 if (open_temp_exec_file_opts_next ())
338 break;
341 while (fd == -1);
343 return fd;
346 /* Map in a chunk of memory from the temporary exec file into separate
347 locations in the virtual memory address space, one writable and one
348 executable. Returns the address of the writable portion, after
349 storing an offset to the corresponding executable portion at the
350 last word of the requested chunk. */
351 static void *
352 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
354 void *ptr;
356 if (execfd == -1)
358 open_temp_exec_file_opts_idx = 0;
359 retry_open:
360 execfd = open_temp_exec_file ();
361 if (execfd == -1)
362 return MFAIL;
365 offset = execsize;
367 if (ftruncate (execfd, offset + length))
368 return MFAIL;
370 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
371 flags |= MAP_SHARED;
373 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
374 flags, execfd, offset);
375 if (ptr == MFAIL)
377 if (!offset)
379 close (execfd);
380 goto retry_open;
382 ftruncate (execfd, offset);
383 return MFAIL;
385 else if (!offset
386 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
387 open_temp_exec_file_opts_next ();
389 start = mmap (start, length, prot, flags, execfd, offset);
391 if (start == MFAIL)
393 munmap (ptr, length);
394 ftruncate (execfd, offset);
395 return start;
398 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
400 execsize += length;
402 return start;
405 /* Map in a writable and executable chunk of memory if possible.
406 Failing that, fall back to dlmmap_locked. */
407 static void *
408 dlmmap (void *start, size_t length, int prot,
409 int flags, int fd, off_t offset)
411 void *ptr;
413 assert (start == NULL && length % malloc_getpagesize == 0
414 && prot == (PROT_READ | PROT_WRITE)
415 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
416 && fd == -1 && offset == 0);
418 #if FFI_CLOSURE_TEST
419 printf ("mapping in %zi\n", length);
420 #endif
422 if (execfd == -1 && !is_selinux_enabled ())
424 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
426 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
427 /* Cool, no need to mess with separate segments. */
428 return ptr;
430 /* If MREMAP_DUP is ever introduced and implemented, try mmap
431 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
432 MREMAP_DUP and prot at this point. */
435 if (execsize == 0 || execfd == -1)
437 pthread_mutex_lock (&open_temp_exec_file_mutex);
438 ptr = dlmmap_locked (start, length, prot, flags, offset);
439 pthread_mutex_unlock (&open_temp_exec_file_mutex);
441 return ptr;
444 return dlmmap_locked (start, length, prot, flags, offset);
447 /* Release memory at the given address, as well as the corresponding
448 executable page if it's separate. */
449 static int
450 dlmunmap (void *start, size_t length)
452 /* We don't bother decreasing execsize or truncating the file, since
453 we can't quite tell whether we're unmapping the end of the file.
454 We don't expect frequent deallocation anyway. If we did, we
455 could locate pages in the file by writing to the pages being
456 deallocated and checking that the file contents change.
457 Yuck. */
458 msegmentptr seg = segment_holding (gm, start);
459 void *code;
461 #if FFI_CLOSURE_TEST
462 printf ("unmapping %zi\n", length);
463 #endif
465 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
467 int ret = munmap (code, length);
468 if (ret)
469 return ret;
472 return munmap (start, length);
475 #if FFI_CLOSURE_FREE_CODE
476 /* Return segment holding given code address. */
477 static msegmentptr
478 segment_holding_code (mstate m, char* addr)
480 msegmentptr sp = &m->seg;
481 for (;;) {
482 if (addr >= add_segment_exec_offset (sp->base, sp)
483 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
484 return sp;
485 if ((sp = sp->next) == 0)
486 return 0;
489 #endif
491 /* Allocate a chunk of memory with the given size. Returns a pointer
492 to the writable address, and sets *CODE to the executable
493 corresponding virtual address. */
494 void *
495 ffi_closure_alloc (size_t size, void **code)
497 void *ptr;
499 if (!code)
500 return NULL;
502 ptr = dlmalloc (size);
504 if (ptr)
506 msegmentptr seg = segment_holding (gm, ptr);
508 *code = add_segment_exec_offset (ptr, seg);
511 return ptr;
514 /* Release a chunk of memory allocated with ffi_closure_alloc. If
515 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
516 writable or the executable address given. Otherwise, only the
517 writable address can be provided here. */
518 void
519 ffi_closure_free (void *ptr)
521 #if FFI_CLOSURE_FREE_CODE
522 msegmentptr seg = segment_holding_code (gm, ptr);
524 if (seg)
525 ptr = sub_segment_exec_offset (ptr, seg);
526 #endif
528 dlfree (ptr);
532 #if FFI_CLOSURE_TEST
533 /* Do some internal sanity testing to make sure allocation and
534 deallocation of pages are working as intended. */
535 int main ()
537 void *p[3];
538 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
539 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
540 GET (0, malloc_getpagesize / 2);
541 GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
542 PUT (1);
543 GET (1, 2 * malloc_getpagesize);
544 GET (2, malloc_getpagesize / 2);
545 PUT (1);
546 PUT (0);
547 PUT (2);
548 return 0;
550 #endif /* FFI_CLOSURE_TEST */
551 # else /* ! FFI_MMAP_EXEC_WRIT */
553 /* On many systems, memory returned by malloc is writable and
554 executable, so just use it. */
556 #include <stdlib.h>
558 void *
559 ffi_closure_alloc (size_t size, void **code)
561 if (!code)
562 return NULL;
564 return *code = malloc (size);
567 void
568 ffi_closure_free (void *ptr)
570 free (ptr);
573 # endif /* ! FFI_MMAP_EXEC_WRIT */
574 #endif /* FFI_CLOSURES */