OSDN Git Service

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