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
33
#include <ffi_common.h>
35
#ifndef FFI_MMAP_EXEC_WRIT
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
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
51
# define FFI_MMAP_EXEC_WRIT 1
55
#if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
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
66
# if FFI_MMAP_EXEC_WRIT
69
#define USE_DL_PREFIX 1
71
#ifndef USE_BUILTIN_FFS
72
#define USE_BUILTIN_FFS 1
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. */
82
/* We have no use for this, so save some code and data. */
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)
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)
100
#include <sys/types.h>
101
#include <sys/stat.h>
109
#if !defined(X86_WIN32) && !defined(X86_WIN64)
112
#endif /* HAVE_MNTENT */
113
#include <sys/param.h>
116
/* We don't want sys/mman.h to be included after we redefine mmap and
118
#include <sys/mman.h>
119
#define LACKS_SYS_MMAN_H 1
121
#if FFI_MMAP_EXEC_SELINUX
122
#include <sys/statfs.h>
125
static int selinux_enabled = -1;
128
selinux_enabled_check (void)
135
if (statfs ("/selinux", &sfs) >= 0
136
&& (unsigned int) sfs.f_type == 0xf97cff8cU)
138
f = fopen ("/proc/mounts", "r");
141
while (getline (&buf, &len, f) >= 0)
143
char *p = strchr (buf, ' ');
146
p = strchr (p + 1, ' ');
149
if (strncmp (p + 1, "selinuxfs ", 10) == 0)
161
#define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
162
: (selinux_enabled = selinux_enabled_check ()))
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__) */
203
#define munmap dlmunmap
205
#include "dlmalloc.c"
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
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. */
224
open_temp_exec_file_name (char *name)
226
int fd = mkstemp (name);
234
/* Open a temporary file in the named directory. */
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));
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
254
open_temp_exec_file_env (const char *envvar)
256
const char *value = getenv (envvar);
261
return open_temp_exec_file_dir (value);
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. */
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)
278
endmntent (last_mntent);
280
last_mounts = mounts;
283
last_mntent = setmntent (mounts, "r");
295
char buf[MAXPATHLEN * 3];
297
if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
300
if (hasmntopt (&mnt, "ro")
301
|| hasmntopt (&mnt, "noexec")
302
|| access (mnt.mnt_dir, W_OK))
305
fd = open_temp_exec_file_dir (mnt.mnt_dir);
311
#endif /* HAVE_MNTENT */
313
/* Instructions to look for a location to hold a temporary file that
314
can be mapped in for execution. */
317
int (*func)(const char *);
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 },
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. */
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;
356
/* Return a file descriptor of a temporary zero-sized file in a
357
writable and exexutable filesystem. */
359
open_temp_exec_file (void)
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
371
if (open_temp_exec_file_opts_next ())
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. */
386
dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
392
open_temp_exec_file_opts_idx = 0;
394
execfd = open_temp_exec_file ();
401
if (ftruncate (execfd, offset + length))
404
flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
407
ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
408
flags, execfd, offset);
416
ftruncate (execfd, 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);
427
munmap (ptr, length);
428
ftruncate (execfd, offset);
432
mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
439
/* Map in a writable and executable chunk of memory if possible.
440
Failing that, fall back to dlmmap_locked. */
442
dlmmap (void *start, size_t length, int prot,
443
int flags, int fd, off_t offset)
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);
453
printf ("mapping in %zi\n", length);
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. */
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);
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. */
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.
492
msegmentptr seg = segment_holding (gm, start);
496
printf ("unmapping %zi\n", length);
499
if (seg && (code = add_segment_exec_offset (start, seg)) != start)
501
int ret = munmap (code, length);
506
return munmap (start, length);
509
#if FFI_CLOSURE_FREE_CODE
510
/* Return segment holding given code address. */
512
segment_holding_code (mstate m, char* addr)
514
msegmentptr sp = &m->seg;
516
if (addr >= add_segment_exec_offset (sp->base, sp)
517
&& addr < add_segment_exec_offset (sp->base, sp) + sp->size)
519
if ((sp = sp->next) == 0)
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. */
531
ffi_closure_alloc (size_t size, void **code)
538
ptr = dlmalloc (size);
542
msegmentptr seg = segment_holding (gm, ptr);
544
*code = add_segment_exec_offset (ptr, seg);
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. */
555
ffi_closure_free (void *ptr)
557
#if FFI_CLOSURE_FREE_CODE
558
msegmentptr seg = segment_holding_code (gm, ptr);
561
ptr = sub_segment_exec_offset (ptr, seg);
569
/* Do some internal sanity testing to make sure allocation and
570
deallocation of pages are working as intended. */
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*));
579
GET (1, 2 * malloc_getpagesize);
580
GET (2, malloc_getpagesize / 2);
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. */
595
ffi_closure_alloc (size_t size, void **code)
600
return *code = malloc (size);
604
ffi_closure_free (void *ptr)
609
# endif /* ! FFI_MMAP_EXEC_WRIT */
610
#endif /* FFI_CLOSURES */