OSDN Git Service

2007-06-13 Eric Christopher <echristo@apple.com>
[pf3gnuchains/gcc-fork.git] / libffi / src / closures.c
1 /* -----------------------------------------------------------------------
2    closures.c - Copyright (c) 2007  Red Hat, Inc.
3
4    Code to allocate and deallocate memory for closures.
5
6    Permission is hereby granted, free of charge, to any person obtaining
7    a copy of this software and associated documentation files (the
8    ``Software''), to deal in the Software without restriction, including
9    without limitation the rights to use, copy, modify, merge, publish,
10    distribute, sublicense, and/or sell copies of the Software, and to
11    permit persons to whom the Software is furnished to do so, subject to
12    the following conditions:
13
14    The above copyright notice and this permission notice shall be included
15    in all copies or substantial portions of the Software.
16
17    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20    IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23    OTHER DEALINGS IN THE SOFTWARE.
24    ----------------------------------------------------------------------- */
25
26 #if defined __linux__ && !defined _GNU_SOURCE
27 #define _GNU_SOURCE 1
28 #endif
29
30 #include <ffi.h>
31 #include <ffi_common.h>
32
33 #ifndef FFI_MMAP_EXEC_WRIT
34 # if __gnu_linux__
35 /* This macro indicates it may be forbidden to map anonymous memory
36    with both write and execute permission.  Code compiled when this
37    option is defined will attempt to map such pages once, but if it
38    fails, it falls back to creating a temporary file in a writable and
39    executable filesystem and mapping pages from it into separate
40    locations in the virtual memory space, one location writable and
41    another executable.  */
42 #  define FFI_MMAP_EXEC_WRIT 1
43 # endif
44 #endif
45
46 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
47 # ifdef __linux__
48 /* When defined to 1 check for SELinux and if SELinux is active,
49    don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
50    might cause audit messages.  */
51 #  define FFI_MMAP_EXEC_SELINUX 1
52 # endif
53 #endif
54
55 #if FFI_CLOSURES
56
57 # if FFI_MMAP_EXEC_WRIT
58
59 #define USE_LOCKS 1
60 #define USE_DL_PREFIX 1
61 #define USE_BUILTIN_FFS 1
62
63 /* We need to use mmap, not sbrk.  */
64 #define HAVE_MORECORE 0
65
66 /* We could, in theory, support mremap, but it wouldn't buy us anything.  */
67 #define HAVE_MREMAP 0
68
69 /* We have no use for this, so save some code and data.  */
70 #define NO_MALLINFO 1
71
72 /* We need all allocations to be in regular segments, otherwise we
73    lose track of the corresponding code address.  */
74 #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
75
76 /* Don't allocate more than a page unless needed.  */
77 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
78
79 #if FFI_CLOSURE_TEST
80 /* Don't release single pages, to avoid a worst-case scenario of
81    continuously allocating and releasing single pages, but release
82    pairs of pages, which should do just as well given that allocations
83    are likely to be small.  */
84 #define DEFAULT_TRIM_THRESHOLD ((size_t)malloc_getpagesize)
85 #endif
86
87 #include <sys/types.h>
88 #include <sys/stat.h>
89 #include <fcntl.h>
90 #include <errno.h>
91 #include <unistd.h>
92 #include <string.h>
93 #include <stdio.h>
94 #include <mntent.h>
95 #include <sys/param.h>
96 #include <pthread.h>
97
98 /* We don't want sys/mman.h to be included after we redefine mmap and
99    dlmunmap.  */
100 #include <sys/mman.h>
101 #define LACKS_SYS_MMAN_H 1
102
103 #if FFI_MMAP_EXEC_SELINUX
104 #include <sys/statfs.h>
105 #include <stdlib.h>
106
107 static int selinux_enabled = -1;
108
109 static int
110 selinux_enabled_check (void)
111 {
112   struct statfs sfs;
113   FILE *f;
114   char *buf = NULL;
115   size_t len = 0;
116
117   if (statfs ("/selinux", &sfs) >= 0
118       && (unsigned int) sfs.f_type == 0xf97cff8cU)
119     return 1;
120   f = fopen ("/proc/mounts", "r");
121   if (f == NULL)
122     return 0;
123   while (getline (&buf, &len, f) >= 0)
124     {
125       char *p = strchr (buf, ' ');
126       if (p == NULL)
127         break;
128       p = strchr (p + 1, ' ');
129       if (p == NULL)
130         break;
131       if (strncmp (p + 1, "selinuxfs ", 10) != 0)
132         {
133           free (buf);
134           fclose (f);
135           return 1;
136         }
137     }
138   free (buf);
139   fclose (f);
140   return 0;
141 }
142
143 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
144                               : (selinux_enabled = selinux_enabled_check ()))
145
146 #else
147
148 #define is_selinux_enabled() 0
149
150 #endif
151
152 #define MAYBE_UNUSED __attribute__((__unused__))
153
154 /* Declare all functions defined in dlmalloc.c as static.  */
155 static void *dlmalloc(size_t);
156 static void dlfree(void*);
157 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
158 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
159 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
160 static void *dlvalloc(size_t) MAYBE_UNUSED;
161 static int dlmallopt(int, int) MAYBE_UNUSED;
162 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
163 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
164 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
165 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
166 static void *dlpvalloc(size_t) MAYBE_UNUSED;
167 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
168 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
169 static void dlmalloc_stats(void) MAYBE_UNUSED;
170
171 /* Use these for mmap and munmap within dlmalloc.c.  */
172 static void *dlmmap(void *, size_t, int, int, int, off_t);
173 static int dlmunmap(void *, size_t);
174
175 #define mmap dlmmap
176 #define munmap dlmunmap
177
178 #include "dlmalloc.c"
179
180 #undef mmap
181 #undef munmap
182
183 /* A mutex used to synchronize access to *exec* variables in this file.  */
184 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
185
186 /* A file descriptor of a temporary file from which we'll map
187    executable pages.  */
188 static int execfd = -1;
189
190 /* The amount of space already allocated from the temporary file.  */
191 static size_t execsize = 0;
192
193 /* Open a temporary file name, and immediately unlink it.  */
194 static int
195 open_temp_exec_file_name (char *name)
196 {
197   int fd = mkstemp (name);
198
199   if (fd != -1)
200     unlink (name);
201
202   return fd;
203 }
204
205 /* Open a temporary file in the named directory.  */
206 static int
207 open_temp_exec_file_dir (const char *dir)
208 {
209   static const char suffix[] = "/ffiXXXXXX";
210   int lendir = strlen (dir);
211   char *tempname = __builtin_alloca (lendir + sizeof (suffix));
212
213   if (!tempname)
214     return -1;
215
216   memcpy (tempname, dir, lendir);
217   memcpy (tempname + lendir, suffix, sizeof (suffix));
218
219   return open_temp_exec_file_name (tempname);
220 }
221
222 /* Open a temporary file in the directory in the named environment
223    variable.  */
224 static int
225 open_temp_exec_file_env (const char *envvar)
226 {
227   const char *value = getenv (envvar);
228
229   if (!value)
230     return -1;
231
232   return open_temp_exec_file_dir (value);
233 }
234
235 /* Open a temporary file in an executable and writable mount point
236    listed in the mounts file.  Subsequent calls with the same mounts
237    keep searching for mount points in the same file.  Providing NULL
238    as the mounts file closes the file.  */
239 static int
240 open_temp_exec_file_mnt (const char *mounts)
241 {
242   static const char *last_mounts;
243   static FILE *last_mntent;
244
245   if (mounts != last_mounts)
246     {
247       if (last_mntent)
248         endmntent (last_mntent);
249
250       last_mounts = mounts;
251
252       if (mounts)
253         last_mntent = setmntent (mounts, "r");
254       else
255         last_mntent = NULL;
256     }
257
258   if (!last_mntent)
259     return -1;
260
261   for (;;)
262     {
263       int fd;
264       struct mntent mnt;
265       char buf[MAXPATHLEN * 3];
266
267       if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)))
268         return -1;
269
270       if (hasmntopt (&mnt, "ro")
271           || hasmntopt (&mnt, "noexec")
272           || access (mnt.mnt_dir, W_OK))
273         continue;
274
275       fd = open_temp_exec_file_dir (mnt.mnt_dir);
276
277       if (fd != -1)
278         return fd;
279     }
280 }
281
282 /* Instructions to look for a location to hold a temporary file that
283    can be mapped in for execution.  */
284 static struct
285 {
286   int (*func)(const char *);
287   const char *arg;
288   int repeat;
289 } open_temp_exec_file_opts[] = {
290   { open_temp_exec_file_env, "TMPDIR", 0 },
291   { open_temp_exec_file_dir, "/tmp", 0 },
292   { open_temp_exec_file_dir, "/var/tmp", 0 },
293   { open_temp_exec_file_dir, "/dev/shm", 0 },
294   { open_temp_exec_file_env, "HOME", 0 },
295   { open_temp_exec_file_mnt, "/etc/mtab", 1 },
296   { open_temp_exec_file_mnt, "/proc/mounts", 1 },
297 };
298
299 /* Current index into open_temp_exec_file_opts.  */
300 static int open_temp_exec_file_opts_idx = 0;
301
302 /* Reset a current multi-call func, then advances to the next entry.
303    If we're at the last, go back to the first and return nonzero,
304    otherwise return zero.  */
305 static int
306 open_temp_exec_file_opts_next (void)
307 {
308   if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
309     open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
310
311   open_temp_exec_file_opts_idx++;
312   if (open_temp_exec_file_opts_idx
313       == (sizeof (open_temp_exec_file_opts)
314           / sizeof (*open_temp_exec_file_opts)))
315     {
316       open_temp_exec_file_opts_idx = 0;
317       return 1;
318     }
319
320   return 0;
321 }
322
323 /* Return a file descriptor of a temporary zero-sized file in a
324    writable and exexutable filesystem.  */
325 static int
326 open_temp_exec_file (void)
327 {
328   int fd;
329
330   do
331     {
332       fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
333         (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
334
335       if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
336           || fd == -1)
337         {
338           if (open_temp_exec_file_opts_next ())
339             break;
340         }
341     }
342   while (fd == -1);
343
344   return fd;
345 }
346
347 /* Map in a chunk of memory from the temporary exec file into separate
348    locations in the virtual memory address space, one writable and one
349    executable.  Returns the address of the writable portion, after
350    storing an offset to the corresponding executable portion at the
351    last word of the requested chunk.  */
352 static void *
353 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
354 {
355   void *ptr;
356
357   if (execfd == -1)
358     {
359       open_temp_exec_file_opts_idx = 0;
360     retry_open:
361       execfd = open_temp_exec_file ();
362       if (execfd == -1)
363         return MFAIL;
364     }
365
366   offset = execsize;
367
368   if (ftruncate (execfd, offset + length))
369     return MFAIL;
370
371   flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
372   flags |= MAP_SHARED;
373
374   ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
375               flags, execfd, offset);
376   if (ptr == MFAIL)
377     {
378       if (!offset)
379         {
380           close (execfd);
381           goto retry_open;
382         }
383       ftruncate (execfd, offset);
384       return MFAIL;
385     }
386   else if (!offset
387            && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
388     open_temp_exec_file_opts_next ();
389
390   start = mmap (start, length, prot, flags, execfd, offset);
391
392   if (start == MFAIL)
393     {
394       munmap (ptr, length);
395       ftruncate (execfd, offset);
396       return start;
397     }
398
399   mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
400
401   execsize += length;
402
403   return start;
404 }
405
406 /* Map in a writable and executable chunk of memory if possible.
407    Failing that, fall back to dlmmap_locked.  */
408 static void *
409 dlmmap (void *start, size_t length, int prot,
410         int flags, int fd, off_t offset)
411 {
412   void *ptr;
413
414   assert (start == NULL && length % malloc_getpagesize == 0
415           && prot == (PROT_READ | PROT_WRITE)
416           && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
417           && fd == -1 && offset == 0);
418
419 #if FFI_CLOSURE_TEST
420   printf ("mapping in %zi\n", length);
421 #endif
422
423   if (execfd == -1 && !is_selinux_enabled ())
424     {
425       ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
426
427       if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
428         /* Cool, no need to mess with separate segments.  */
429         return ptr;
430
431       /* If MREMAP_DUP is ever introduced and implemented, try mmap
432          with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
433          MREMAP_DUP and prot at this point.  */
434     }
435
436   if (execsize == 0 || execfd == -1)
437     {
438       pthread_mutex_lock (&open_temp_exec_file_mutex);
439       ptr = dlmmap_locked (start, length, prot, flags, offset);
440       pthread_mutex_unlock (&open_temp_exec_file_mutex);
441
442       return ptr;
443     }
444
445   return dlmmap_locked (start, length, prot, flags, offset);
446 }
447
448 /* Release memory at the given address, as well as the corresponding
449    executable page if it's separate.  */
450 static int
451 dlmunmap (void *start, size_t length)
452 {
453   /* We don't bother decreasing execsize or truncating the file, since
454      we can't quite tell whether we're unmapping the end of the file.
455      We don't expect frequent deallocation anyway.  If we did, we
456      could locate pages in the file by writing to the pages being
457      deallocated and checking that the file contents change.
458      Yuck.  */
459   msegmentptr seg = segment_holding (gm, start);
460   void *code;
461
462 #if FFI_CLOSURE_TEST
463   printf ("unmapping %zi\n", length);
464 #endif
465
466   if (seg && (code = add_segment_exec_offset (start, seg)) != start)
467     {
468       int ret = munmap (code, length);
469       if (ret)
470         return ret;
471     }
472
473   return munmap (start, length);
474 }
475
476 #if FFI_CLOSURE_FREE_CODE
477 /* Return segment holding given code address.  */
478 static msegmentptr
479 segment_holding_code (mstate m, char* addr)
480 {
481   msegmentptr sp = &m->seg;
482   for (;;) {
483     if (addr >= add_segment_exec_offset (sp->base, sp)
484         && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
485       return sp;
486     if ((sp = sp->next) == 0)
487       return 0;
488   }
489 }
490 #endif
491
492 /* Allocate a chunk of memory with the given size.  Returns a pointer
493    to the writable address, and sets *CODE to the executable
494    corresponding virtual address.  */
495 void *
496 ffi_closure_alloc (size_t size, void **code)
497 {
498   void *ptr;
499
500   if (!code)
501     return NULL;
502
503   ptr = dlmalloc (size);
504
505   if (ptr)
506     {
507       msegmentptr seg = segment_holding (gm, ptr);
508
509       *code = add_segment_exec_offset (ptr, seg);
510     }
511
512   return ptr;
513 }
514
515 /* Release a chunk of memory allocated with ffi_closure_alloc.  If
516    FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
517    writable or the executable address given.  Otherwise, only the
518    writable address can be provided here.  */
519 void
520 ffi_closure_free (void *ptr)
521 {
522 #if FFI_CLOSURE_FREE_CODE
523   msegmentptr seg = segment_holding_code (gm, ptr);
524
525   if (seg)
526     ptr = sub_segment_exec_offset (ptr, seg);
527 #endif
528
529   dlfree (ptr);
530 }
531
532
533 #if FFI_CLOSURE_TEST
534 /* Do some internal sanity testing to make sure allocation and
535    deallocation of pages are working as intended.  */
536 int main ()
537 {
538   void *p[3];
539 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
540 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
541   GET (0, malloc_getpagesize / 2);
542   GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
543   PUT (1);
544   GET (1, 2 * malloc_getpagesize);
545   GET (2, malloc_getpagesize / 2);
546   PUT (1);
547   PUT (0);
548   PUT (2);
549   return 0;
550 }
551 #endif /* FFI_CLOSURE_TEST */
552 # else /* ! FFI_MMAP_EXEC_WRIT */
553
554 /* On many systems, memory returned by malloc is writable and
555    executable, so just use it.  */
556
557 #include <stdlib.h>
558
559 void *
560 ffi_closure_alloc (size_t size, void **code)
561 {
562   if (!code)
563     return NULL;
564
565   return *code = malloc (size);
566 }
567
568 void
569 ffi_closure_free (void *ptr)
570 {
571   free (ptr);
572 }
573
574 # endif /* ! FFI_MMAP_EXEC_WRIT */
575 #endif /* FFI_CLOSURES */