]> git.ipfire.org Git - thirdparty/gcc.git/blame - libffi/src/closures.c
re PR libffi/40700 (All amd64 libffi execution tests fail on Solaris 10/x86)
[thirdparty/gcc.git] / libffi / src / closures.c
CommitLineData
18fa3240
AO
1/* -----------------------------------------------------------------------
2 closures.c - Copyright (c) 2007 Red Hat, Inc.
dcb00f2e 3 Copyright (C) 2007, 2009 Free Software Foundation, Inc
18fa3240
AO
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
5f933ef0
AH
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.
18fa3240
AO
26 ----------------------------------------------------------------------- */
27
f0920e6c
JJ
28#if defined __linux__ && !defined _GNU_SOURCE
29#define _GNU_SOURCE 1
30#endif
31
18fa3240
AO
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
062b8279
AH
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
18fa3240 52# endif
dcb00f2e
RO
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
18fa3240
AO
58#endif
59
f0920e6c
JJ
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
18fa3240
AO
69#if FFI_CLOSURES
70
71# if FFI_MMAP_EXEC_WRIT
72
73#define USE_LOCKS 1
74#define USE_DL_PREFIX 1
062b8279
AH
75#ifdef __GNUC__
76#ifndef USE_BUILTIN_FFS
18fa3240 77#define USE_BUILTIN_FFS 1
062b8279
AH
78#endif
79#endif
18fa3240
AO
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>
062b8279 109#ifndef _MSC_VER
18fa3240 110#include <unistd.h>
062b8279 111#endif
18fa3240
AO
112#include <string.h>
113#include <stdio.h>
062b8279
AH
114#if !defined(X86_WIN32) && !defined(X86_WIN64)
115#ifdef HAVE_MNTENT
18fa3240 116#include <mntent.h>
062b8279 117#endif /* HAVE_MNTENT */
18fa3240
AO
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
f0920e6c
JJ
126#if FFI_MMAP_EXEC_SELINUX
127#include <sys/statfs.h>
128#include <stdlib.h>
129
130static int selinux_enabled = -1;
131
132static int
133selinux_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
723512ba
DK
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
062b8279 182#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
f0920e6c 183
18fa3240
AO
184/* Declare all functions defined in dlmalloc.c as static. */
185static void *dlmalloc(size_t);
186static void dlfree(void*);
187static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
188static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
189static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
190static void *dlvalloc(size_t) MAYBE_UNUSED;
191static int dlmallopt(int, int) MAYBE_UNUSED;
192static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
193static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
194static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
195static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
196static void *dlpvalloc(size_t) MAYBE_UNUSED;
197static int dlmalloc_trim(size_t) MAYBE_UNUSED;
198static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
199static void dlmalloc_stats(void) MAYBE_UNUSED;
200
723512ba 201#if !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__)
18fa3240
AO
202/* Use these for mmap and munmap within dlmalloc.c. */
203static void *dlmmap(void *, size_t, int, int, int, off_t);
204static int dlmunmap(void *, size_t);
723512ba 205#endif /* !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__) */
18fa3240
AO
206
207#define mmap dlmmap
208#define munmap dlmunmap
209
210#include "dlmalloc.c"
211
212#undef mmap
213#undef munmap
214
723512ba 215#if !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__)
062b8279 216
18fa3240
AO
217/* A mutex used to synchronize access to *exec* variables in this file. */
218static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
219
220/* A file descriptor of a temporary file from which we'll map
221 executable pages. */
222static int execfd = -1;
223
224/* The amount of space already allocated from the temporary file. */
225static size_t execsize = 0;
226
227/* Open a temporary file name, and immediately unlink it. */
228static int
229open_temp_exec_file_name (char *name)
230{
231 int fd = mkstemp (name);
232
233 if (fd != -1)
234 unlink (name);
235
236 return fd;
237}
238
239/* Open a temporary file in the named directory. */
240static int
241open_temp_exec_file_dir (const char *dir)
242{
243 static const char suffix[] = "/ffiXXXXXX";
244 int lendir = strlen (dir);
245 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
246
247 if (!tempname)
248 return -1;
249
250 memcpy (tempname, dir, lendir);
251 memcpy (tempname + lendir, suffix, sizeof (suffix));
252
253 return open_temp_exec_file_name (tempname);
254}
255
256/* Open a temporary file in the directory in the named environment
257 variable. */
258static int
259open_temp_exec_file_env (const char *envvar)
260{
261 const char *value = getenv (envvar);
262
263 if (!value)
264 return -1;
265
266 return open_temp_exec_file_dir (value);
267}
268
062b8279 269#ifdef HAVE_MNTENT
18fa3240
AO
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. */
274static int
275open_temp_exec_file_mnt (const char *mounts)
276{
277 static const char *last_mounts;
278 static FILE *last_mntent;
279
280 if (mounts != last_mounts)
281 {
282 if (last_mntent)
283 endmntent (last_mntent);
284
285 last_mounts = mounts;
286
287 if (mounts)
288 last_mntent = setmntent (mounts, "r");
289 else
290 last_mntent = NULL;
291 }
292
293 if (!last_mntent)
294 return -1;
295
296 for (;;)
297 {
298 int fd;
299 struct mntent mnt;
300 char buf[MAXPATHLEN * 3];
301
302 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
303 return -1;
304
305 if (hasmntopt (&mnt, "ro")
306 || hasmntopt (&mnt, "noexec")
307 || access (mnt.mnt_dir, W_OK))
308 continue;
309
310 fd = open_temp_exec_file_dir (mnt.mnt_dir);
311
312 if (fd != -1)
313 return fd;
314 }
315}
062b8279 316#endif /* HAVE_MNTENT */
18fa3240
AO
317
318/* Instructions to look for a location to hold a temporary file that
319 can be mapped in for execution. */
320static struct
321{
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 },
062b8279 331#ifdef HAVE_MNTENT
18fa3240
AO
332 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
333 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
062b8279 334#endif /* HAVE_MNTENT */
18fa3240
AO
335};
336
337/* Current index into open_temp_exec_file_opts. */
338static int open_temp_exec_file_opts_idx = 0;
339
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. */
343static int
344open_temp_exec_file_opts_next (void)
345{
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);
348
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)))
353 {
354 open_temp_exec_file_opts_idx = 0;
355 return 1;
356 }
357
358 return 0;
359}
360
361/* Return a file descriptor of a temporary zero-sized file in a
362 writable and exexutable filesystem. */
363static int
364open_temp_exec_file (void)
365{
366 int fd;
367
368 do
369 {
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);
372
373 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
374 || fd == -1)
375 {
376 if (open_temp_exec_file_opts_next ())
377 break;
378 }
379 }
380 while (fd == -1);
381
382 return fd;
383}
384
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. */
390static void *
391dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
392{
393 void *ptr;
394
395 if (execfd == -1)
396 {
397 open_temp_exec_file_opts_idx = 0;
398 retry_open:
399 execfd = open_temp_exec_file ();
400 if (execfd == -1)
401 return MFAIL;
402 }
403
404 offset = execsize;
405
406 if (ftruncate (execfd, offset + length))
407 return MFAIL;
408
409 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
410 flags |= MAP_SHARED;
411
412 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
413 flags, execfd, offset);
414 if (ptr == MFAIL)
415 {
416 if (!offset)
417 {
418 close (execfd);
419 goto retry_open;
420 }
421 ftruncate (execfd, offset);
422 return MFAIL;
423 }
424 else if (!offset
425 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
426 open_temp_exec_file_opts_next ();
427
428 start = mmap (start, length, prot, flags, execfd, offset);
429
430 if (start == MFAIL)
431 {
432 munmap (ptr, length);
433 ftruncate (execfd, offset);
434 return start;
435 }
436
437 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
438
439 execsize += length;
440
441 return start;
442}
443
444/* Map in a writable and executable chunk of memory if possible.
445 Failing that, fall back to dlmmap_locked. */
446static void *
447dlmmap (void *start, size_t length, int prot,
448 int flags, int fd, off_t offset)
449{
450 void *ptr;
451
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);
456
457#if FFI_CLOSURE_TEST
458 printf ("mapping in %zi\n", length);
459#endif
460
f0920e6c 461 if (execfd == -1 && !is_selinux_enabled ())
18fa3240
AO
462 {
463 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
464
465 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
466 /* Cool, no need to mess with separate segments. */
467 return ptr;
468
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. */
472 }
473
474 if (execsize == 0 || execfd == -1)
475 {
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);
479
480 return ptr;
481 }
482
483 return dlmmap_locked (start, length, prot, flags, offset);
484}
485
486/* Release memory at the given address, as well as the corresponding
487 executable page if it's separate. */
488static int
489dlmunmap (void *start, size_t length)
490{
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;
499
500#if FFI_CLOSURE_TEST
501 printf ("unmapping %zi\n", length);
502#endif
503
504 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
505 {
506 int ret = munmap (code, length);
507 if (ret)
508 return ret;
509 }
510
511 return munmap (start, length);
512}
513
514#if FFI_CLOSURE_FREE_CODE
515/* Return segment holding given code address. */
516static msegmentptr
517segment_holding_code (mstate m, char* addr)
518{
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;
526 }
527}
528#endif
529
723512ba 530#endif /* !(defined(X86_WIN32) || defined(X86_WIN64)) || defined (__CYGWIN__) */
062b8279 531
18fa3240
AO
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. */
535void *
536ffi_closure_alloc (size_t size, void **code)
537{
538 void *ptr;
539
540 if (!code)
541 return NULL;
542
543 ptr = dlmalloc (size);
544
545 if (ptr)
546 {
547 msegmentptr seg = segment_holding (gm, ptr);
548
549 *code = add_segment_exec_offset (ptr, seg);
550 }
551
552 return ptr;
553}
554
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. */
559void
560ffi_closure_free (void *ptr)
561{
562#if FFI_CLOSURE_FREE_CODE
563 msegmentptr seg = segment_holding_code (gm, ptr);
564
565 if (seg)
566 ptr = sub_segment_exec_offset (ptr, seg);
567#endif
568
569 dlfree (ptr);
570}
571
572
573#if FFI_CLOSURE_TEST
574/* Do some internal sanity testing to make sure allocation and
575 deallocation of pages are working as intended. */
576int main ()
577{
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;
590}
591#endif /* FFI_CLOSURE_TEST */
592# else /* ! FFI_MMAP_EXEC_WRIT */
593
594/* On many systems, memory returned by malloc is writable and
595 executable, so just use it. */
596
597#include <stdlib.h>
598
599void *
600ffi_closure_alloc (size_t size, void **code)
601{
602 if (!code)
603 return NULL;
604
605 return *code = malloc (size);
606}
607
608void
609ffi_closure_free (void *ptr)
610{
611 free (ptr);
612}
613
614# endif /* ! FFI_MMAP_EXEC_WRIT */
615#endif /* FFI_CLOSURES */