Update missing changes for 3.0.9r4.
[libffi.git] / src / closures.c
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)
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 # if defined(X86_64) && defined(__sun__) && defined(__svr4__)
54 /* The data segment on 64-bit Solaris/x86 isn't executable, so use mmap
55 instead. */
56 # define FFI_MMAP_EXEC_WRIT 1
57 # endif
58 #endif
59
60 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
61 # ifdef __linux__
62 /* When defined to 1 check for SELinux and if SELinux is active,
63 don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
64 might cause audit messages. */
65 # define FFI_MMAP_EXEC_SELINUX 1
66 # endif
67 #endif
68
69 #if FFI_CLOSURES
70
71 # if FFI_MMAP_EXEC_WRIT
72
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
80
81 /* We need to use mmap, not sbrk. */
82 #define HAVE_MORECORE 0
83
84 /* We could, in theory, support mremap, but it wouldn't buy us anything. */
85 #define HAVE_MREMAP 0
86
87 /* We have no use for this, so save some code and data. */
88 #define NO_MALLINFO 1
89
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
93
94 /* Don't allocate more than a page unless needed. */
95 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
96
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
104
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>
120
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
125
126 #if FFI_MMAP_EXEC_SELINUX
127 #include <sys/statfs.h>
128 #include <stdlib.h>
129
130 static int selinux_enabled = -1;
131
132 static int
133 selinux_enabled_check (void)
134 {
135 struct statfs sfs;
136 FILE *f;
137 char *buf = NULL;
138 size_t len = 0;
139
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)
147 {
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)
155 {
156 free (buf);
157 fclose (f);
158 return 1;
159 }
160 }
161 free (buf);
162 fclose (f);
163 return 0;
164 }
165
166 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
167 : (selinux_enabled = selinux_enabled_check ()))
168
169 #else
170
171 #define is_selinux_enabled() 0
172
173 #endif /* !FFI_MMAP_EXEC_SELINUX */
174
175 #elif defined (__CYGWIN__)
176
177 #include <sys/mman.h>
178
179 /* Cygwin is Linux-like, but not quite that Linux-like. */
180 #define is_selinux_enabled() 0
181
182 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
183
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;
200
201 #if !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__)
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 (__CYGWIN__) */
206
207 #define mmap dlmmap
208 #define munmap dlmunmap
209
210 #include "dlmalloc.c"
211
212 #undef mmap
213 #undef munmap
214
215 #if !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__)
216
217 #if FFI_MMAP_EXEC_SELINUX
218
219 /* A mutex used to synchronize access to *exec* variables in this file. */
220 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
221
222 /* A file descriptor of a temporary file from which we'll map
223 executable pages. */
224 static int execfd = -1;
225
226 /* The amount of space already allocated from the temporary file. */
227 static size_t execsize = 0;
228
229 /* Open a temporary file name, and immediately unlink it. */
230 static int
231 open_temp_exec_file_name (char *name)
232 {
233 int fd = mkstemp (name);
234
235 if (fd != -1)
236 unlink (name);
237
238 return fd;
239 }
240
241 /* Open a temporary file in the named directory. */
242 static int
243 open_temp_exec_file_dir (const char *dir)
244 {
245 static const char suffix[] = "/ffiXXXXXX";
246 int lendir = strlen (dir);
247 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
248
249 if (!tempname)
250 return -1;
251
252 memcpy (tempname, dir, lendir);
253 memcpy (tempname + lendir, suffix, sizeof (suffix));
254
255 return open_temp_exec_file_name (tempname);
256 }
257
258 /* Open a temporary file in the directory in the named environment
259 variable. */
260 static int
261 open_temp_exec_file_env (const char *envvar)
262 {
263 const char *value = getenv (envvar);
264
265 if (!value)
266 return -1;
267
268 return open_temp_exec_file_dir (value);
269 }
270
271 #ifdef HAVE_MNTENT
272 /* Open a temporary file in an executable and writable mount point
273 listed in the mounts file. Subsequent calls with the same mounts
274 keep searching for mount points in the same file. Providing NULL
275 as the mounts file closes the file. */
276 static int
277 open_temp_exec_file_mnt (const char *mounts)
278 {
279 static const char *last_mounts;
280 static FILE *last_mntent;
281
282 if (mounts != last_mounts)
283 {
284 if (last_mntent)
285 endmntent (last_mntent);
286
287 last_mounts = mounts;
288
289 if (mounts)
290 last_mntent = setmntent (mounts, "r");
291 else
292 last_mntent = NULL;
293 }
294
295 if (!last_mntent)
296 return -1;
297
298 for (;;)
299 {
300 int fd;
301 struct mntent mnt;
302 char buf[MAXPATHLEN * 3];
303
304 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
305 return -1;
306
307 if (hasmntopt (&mnt, "ro")
308 || hasmntopt (&mnt, "noexec")
309 || access (mnt.mnt_dir, W_OK))
310 continue;
311
312 fd = open_temp_exec_file_dir (mnt.mnt_dir);
313
314 if (fd != -1)
315 return fd;
316 }
317 }
318 #endif /* HAVE_MNTENT */
319
320 /* Instructions to look for a location to hold a temporary file that
321 can be mapped in for execution. */
322 static struct
323 {
324 int (*func)(const char *);
325 const char *arg;
326 int repeat;
327 } open_temp_exec_file_opts[] = {
328 { open_temp_exec_file_env, "TMPDIR", 0 },
329 { open_temp_exec_file_dir, "/tmp", 0 },
330 { open_temp_exec_file_dir, "/var/tmp", 0 },
331 { open_temp_exec_file_dir, "/dev/shm", 0 },
332 { open_temp_exec_file_env, "HOME", 0 },
333 #ifdef HAVE_MNTENT
334 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
335 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
336 #endif /* HAVE_MNTENT */
337 };
338
339 /* Current index into open_temp_exec_file_opts. */
340 static int open_temp_exec_file_opts_idx = 0;
341
342 /* Reset a current multi-call func, then advances to the next entry.
343 If we're at the last, go back to the first and return nonzero,
344 otherwise return zero. */
345 static int
346 open_temp_exec_file_opts_next (void)
347 {
348 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
349 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
350
351 open_temp_exec_file_opts_idx++;
352 if (open_temp_exec_file_opts_idx
353 == (sizeof (open_temp_exec_file_opts)
354 / sizeof (*open_temp_exec_file_opts)))
355 {
356 open_temp_exec_file_opts_idx = 0;
357 return 1;
358 }
359
360 return 0;
361 }
362
363 /* Return a file descriptor of a temporary zero-sized file in a
364 writable and exexutable filesystem. */
365 static int
366 open_temp_exec_file (void)
367 {
368 int fd;
369
370 do
371 {
372 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
373 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
374
375 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
376 || fd == -1)
377 {
378 if (open_temp_exec_file_opts_next ())
379 break;
380 }
381 }
382 while (fd == -1);
383
384 return fd;
385 }
386
387 /* Map in a chunk of memory from the temporary exec file into separate
388 locations in the virtual memory address space, one writable and one
389 executable. Returns the address of the writable portion, after
390 storing an offset to the corresponding executable portion at the
391 last word of the requested chunk. */
392 static void *
393 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
394 {
395 void *ptr;
396
397 if (execfd == -1)
398 {
399 open_temp_exec_file_opts_idx = 0;
400 retry_open:
401 execfd = open_temp_exec_file ();
402 if (execfd == -1)
403 return MFAIL;
404 }
405
406 offset = execsize;
407
408 if (ftruncate (execfd, offset + length))
409 return MFAIL;
410
411 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
412 flags |= MAP_SHARED;
413
414 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
415 flags, execfd, offset);
416 if (ptr == MFAIL)
417 {
418 if (!offset)
419 {
420 close (execfd);
421 goto retry_open;
422 }
423 ftruncate (execfd, offset);
424 return MFAIL;
425 }
426 else if (!offset
427 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
428 open_temp_exec_file_opts_next ();
429
430 start = mmap (start, length, prot, flags, execfd, offset);
431
432 if (start == MFAIL)
433 {
434 munmap (ptr, length);
435 ftruncate (execfd, offset);
436 return start;
437 }
438
439 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
440
441 execsize += length;
442
443 return start;
444 }
445
446 /* Map in a writable and executable chunk of memory if possible.
447 Failing that, fall back to dlmmap_locked. */
448 static void *
449 dlmmap (void *start, size_t length, int prot,
450 int flags, int fd, off_t offset)
451 {
452 void *ptr;
453
454 assert (start == NULL && length % malloc_getpagesize == 0
455 && prot == (PROT_READ | PROT_WRITE)
456 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
457 && fd == -1 && offset == 0);
458
459 #if FFI_CLOSURE_TEST
460 printf ("mapping in %zi\n", length);
461 #endif
462
463 if (execfd == -1 && !is_selinux_enabled ())
464 {
465 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
466
467 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
468 /* Cool, no need to mess with separate segments. */
469 return ptr;
470
471 /* If MREMAP_DUP is ever introduced and implemented, try mmap
472 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
473 MREMAP_DUP and prot at this point. */
474 }
475
476 if (execsize == 0 || execfd == -1)
477 {
478 pthread_mutex_lock (&open_temp_exec_file_mutex);
479 ptr = dlmmap_locked (start, length, prot, flags, offset);
480 pthread_mutex_unlock (&open_temp_exec_file_mutex);
481
482 return ptr;
483 }
484
485 return dlmmap_locked (start, length, prot, flags, offset);
486 }
487
488 #else
489
490 static void *
491 dlmmap (void *start, size_t length, int prot,
492 int flags, int fd, off_t offset)
493 {
494
495 assert (start == NULL && length % malloc_getpagesize == 0
496 && prot == (PROT_READ | PROT_WRITE)
497 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
498 && fd == -1 && offset == 0);
499
500 #if FFI_CLOSURE_TEST
501 printf ("mapping in %zi\n", length);
502 #endif
503
504 return mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
505 }
506
507 #endif
508
509 /* Release memory at the given address, as well as the corresponding
510 executable page if it's separate. */
511 static int
512 dlmunmap (void *start, size_t length)
513 {
514 /* We don't bother decreasing execsize or truncating the file, since
515 we can't quite tell whether we're unmapping the end of the file.
516 We don't expect frequent deallocation anyway. If we did, we
517 could locate pages in the file by writing to the pages being
518 deallocated and checking that the file contents change.
519 Yuck. */
520 msegmentptr seg = segment_holding (gm, start);
521 void *code;
522
523 #if FFI_CLOSURE_TEST
524 printf ("unmapping %zi\n", length);
525 #endif
526
527 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
528 {
529 int ret = munmap (code, length);
530 if (ret)
531 return ret;
532 }
533
534 return munmap (start, length);
535 }
536
537 #if FFI_CLOSURE_FREE_CODE
538 /* Return segment holding given code address. */
539 static msegmentptr
540 segment_holding_code (mstate m, char* addr)
541 {
542 msegmentptr sp = &m->seg;
543 for (;;) {
544 if (addr >= add_segment_exec_offset (sp->base, sp)
545 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
546 return sp;
547 if ((sp = sp->next) == 0)
548 return 0;
549 }
550 }
551 #endif
552
553 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__) */
554
555 /* Allocate a chunk of memory with the given size. Returns a pointer
556 to the writable address, and sets *CODE to the executable
557 corresponding virtual address. */
558 void *
559 ffi_closure_alloc (size_t size, void **code)
560 {
561 void *ptr;
562
563 if (!code)
564 return NULL;
565
566 ptr = dlmalloc (size);
567
568 if (ptr)
569 {
570 msegmentptr seg = segment_holding (gm, ptr);
571
572 *code = add_segment_exec_offset (ptr, seg);
573 }
574
575 return ptr;
576 }
577
578 /* Release a chunk of memory allocated with ffi_closure_alloc. If
579 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
580 writable or the executable address given. Otherwise, only the
581 writable address can be provided here. */
582 void
583 ffi_closure_free (void *ptr)
584 {
585 #if FFI_CLOSURE_FREE_CODE
586 msegmentptr seg = segment_holding_code (gm, ptr);
587
588 if (seg)
589 ptr = sub_segment_exec_offset (ptr, seg);
590 #endif
591
592 dlfree (ptr);
593 }
594
595
596 #if FFI_CLOSURE_TEST
597 /* Do some internal sanity testing to make sure allocation and
598 deallocation of pages are working as intended. */
599 int main ()
600 {
601 void *p[3];
602 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
603 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
604 GET (0, malloc_getpagesize / 2);
605 GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
606 PUT (1);
607 GET (1, 2 * malloc_getpagesize);
608 GET (2, malloc_getpagesize / 2);
609 PUT (1);
610 PUT (0);
611 PUT (2);
612 return 0;
613 }
614 #endif /* FFI_CLOSURE_TEST */
615 # else /* ! FFI_MMAP_EXEC_WRIT */
616
617 /* On many systems, memory returned by malloc is writable and
618 executable, so just use it. */
619
620 #include <stdlib.h>
621
622 void *
623 ffi_closure_alloc (size_t size, void **code)
624 {
625 if (!code)
626 return NULL;
627
628 return *code = malloc (size);
629 }
630
631 void
632 ffi_closure_free (void *ptr)
633 {
634 free (ptr);
635 }
636
637 # endif /* ! FFI_MMAP_EXEC_WRIT */
638 #endif /* FFI_CLOSURES */