Line data Source code
1 : /* -----------------------------------------------------------------------
2 : closures.c - Copyright (c) 2007, 2009, 2010 Red Hat, Inc.
3 : Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
4 : Copyright (c) 2011 Plausible Labs Cooperative, Inc.
5 :
6 : Code to allocate and deallocate memory for closures.
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining
9 : a copy of this software and associated documentation files (the
10 : ``Software''), to deal in the Software without restriction, including
11 : without limitation the rights to use, copy, modify, merge, publish,
12 : distribute, sublicense, and/or sell copies of the Software, and to
13 : permit persons to whom the Software is furnished to do so, subject to
14 : the following conditions:
15 :
16 : The above copyright notice and this permission notice shall be included
17 : in all copies or substantial portions of the Software.
18 :
19 : THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
20 : EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 : MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 : NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 : HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 : WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : DEALINGS IN THE SOFTWARE.
27 : ----------------------------------------------------------------------- */
28 :
29 : #if defined __linux__ && !defined _GNU_SOURCE
30 : #define _GNU_SOURCE 1
31 : #endif
32 :
33 : #include <ffi.h>
34 : #include <ffi_common.h>
35 :
36 : #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
37 : # if __gnu_linux__ && !defined(__ANDROID__)
38 : /* This macro indicates it may be forbidden to map anonymous memory
39 : with both write and execute permission. Code compiled when this
40 : option is defined will attempt to map such pages once, but if it
41 : fails, it falls back to creating a temporary file in a writable and
42 : executable filesystem and mapping pages from it into separate
43 : locations in the virtual memory space, one location writable and
44 : another executable. */
45 : # define FFI_MMAP_EXEC_WRIT 1
46 : # define HAVE_MNTENT 1
47 : # endif
48 : # if defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)
49 : /* Windows systems may have Data Execution Protection (DEP) enabled,
50 : which requires the use of VirtualMalloc/VirtualFree to alloc/free
51 : executable memory. */
52 : # define FFI_MMAP_EXEC_WRIT 1
53 : # endif
54 : #endif
55 :
56 : #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
57 : # ifdef __linux__
58 : /* When defined to 1 check for SELinux and if SELinux is active,
59 : don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
60 : might cause audit messages. */
61 : # define FFI_MMAP_EXEC_SELINUX 1
62 : # endif
63 : #endif
64 :
65 : #if FFI_CLOSURES
66 :
67 : # if FFI_EXEC_TRAMPOLINE_TABLE
68 :
69 : // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
70 :
71 : # elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
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 0 : selinux_enabled_check (void)
134 : {
135 : struct statfs sfs;
136 : FILE *f;
137 0 : char *buf = NULL;
138 0 : size_t len = 0;
139 :
140 0 : if (statfs ("/selinux", &sfs) >= 0
141 0 : && (unsigned int) sfs.f_type == 0xf97cff8cU)
142 0 : return 1;
143 0 : f = fopen ("/proc/mounts", "r");
144 0 : if (f == NULL)
145 0 : return 0;
146 0 : while (getline (&buf, &len, f) >= 0)
147 : {
148 0 : char *p = strchr (buf, ' ');
149 0 : if (p == NULL)
150 0 : break;
151 0 : p = strchr (p + 1, ' ');
152 0 : if (p == NULL)
153 0 : break;
154 0 : if (strncmp (p + 1, "selinuxfs ", 10) == 0)
155 : {
156 0 : free (buf);
157 0 : fclose (f);
158 0 : return 1;
159 : }
160 : }
161 0 : free (buf);
162 0 : fclose (f);
163 0 : 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 : /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
176 : #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
177 : #include <stdlib.h>
178 :
179 : static int emutramp_enabled = -1;
180 :
181 : static int
182 : emutramp_enabled_check (void)
183 : {
184 : char *buf = NULL;
185 : size_t len = 0;
186 : FILE *f;
187 : int ret;
188 : f = fopen ("/proc/self/status", "r");
189 : if (f == NULL)
190 : return 0;
191 : ret = 0;
192 :
193 : while (getline (&buf, &len, f) != -1)
194 : if (!strncmp (buf, "PaX:", 4))
195 : {
196 : char emutramp;
197 : if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
198 : ret = (emutramp == 'E');
199 : break;
200 : }
201 : free (buf);
202 : fclose (f);
203 : return ret;
204 : }
205 :
206 : #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
207 : : (emutramp_enabled = emutramp_enabled_check ()))
208 : #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
209 :
210 : #elif defined (__CYGWIN__) || defined(__INTERIX)
211 :
212 : #include <sys/mman.h>
213 :
214 : /* Cygwin is Linux-like, but not quite that Linux-like. */
215 : #define is_selinux_enabled() 0
216 :
217 : #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
218 :
219 : #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
220 : #define is_emutramp_enabled() 0
221 : #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
222 :
223 : /* Declare all functions defined in dlmalloc.c as static. */
224 : static void *dlmalloc(size_t);
225 : static void dlfree(void*);
226 : static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
227 : static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
228 : static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
229 : static void *dlvalloc(size_t) MAYBE_UNUSED;
230 : static int dlmallopt(int, int) MAYBE_UNUSED;
231 : static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
232 : static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
233 : static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
234 : static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
235 : static void *dlpvalloc(size_t) MAYBE_UNUSED;
236 : static int dlmalloc_trim(size_t) MAYBE_UNUSED;
237 : static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
238 : static void dlmalloc_stats(void) MAYBE_UNUSED;
239 :
240 : #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
241 : /* Use these for mmap and munmap within dlmalloc.c. */
242 : static void *dlmmap(void *, size_t, int, int, int, off_t);
243 : static int dlmunmap(void *, size_t);
244 : #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
245 :
246 : #define mmap dlmmap
247 : #define munmap dlmunmap
248 :
249 : #include "dlmalloc.c"
250 :
251 : #undef mmap
252 : #undef munmap
253 :
254 : #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
255 :
256 : /* A mutex used to synchronize access to *exec* variables in this file. */
257 : static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
258 :
259 : /* A file descriptor of a temporary file from which we'll map
260 : executable pages. */
261 : static int execfd = -1;
262 :
263 : /* The amount of space already allocated from the temporary file. */
264 : static size_t execsize = 0;
265 :
266 : /* Open a temporary file name, and immediately unlink it. */
267 : static int
268 0 : open_temp_exec_file_name (char *name)
269 : {
270 0 : int fd = mkstemp (name);
271 :
272 0 : if (fd != -1)
273 0 : unlink (name);
274 :
275 0 : return fd;
276 : }
277 :
278 : /* Open a temporary file in the named directory. */
279 : static int
280 0 : open_temp_exec_file_dir (const char *dir)
281 : {
282 : static const char suffix[] = "/ffiXXXXXX";
283 0 : size_t lendir = strlen (dir);
284 0 : char *tempname = __builtin_alloca (lendir + sizeof (suffix));
285 :
286 0 : if (!tempname)
287 0 : return -1;
288 :
289 0 : memcpy (tempname, dir, lendir);
290 0 : memcpy (tempname + lendir, suffix, sizeof (suffix));
291 :
292 0 : return open_temp_exec_file_name (tempname);
293 : }
294 :
295 : /* Open a temporary file in the directory in the named environment
296 : variable. */
297 : static int
298 0 : open_temp_exec_file_env (const char *envvar)
299 : {
300 0 : const char *value = getenv (envvar);
301 :
302 0 : if (!value)
303 0 : return -1;
304 :
305 0 : return open_temp_exec_file_dir (value);
306 : }
307 :
308 : #ifdef HAVE_MNTENT
309 : /* Open a temporary file in an executable and writable mount point
310 : listed in the mounts file. Subsequent calls with the same mounts
311 : keep searching for mount points in the same file. Providing NULL
312 : as the mounts file closes the file. */
313 : static int
314 0 : open_temp_exec_file_mnt (const char *mounts)
315 : {
316 : static const char *last_mounts;
317 : static FILE *last_mntent;
318 :
319 0 : if (mounts != last_mounts)
320 : {
321 0 : if (last_mntent)
322 0 : endmntent (last_mntent);
323 :
324 0 : last_mounts = mounts;
325 :
326 0 : if (mounts)
327 0 : last_mntent = setmntent (mounts, "r");
328 : else
329 0 : last_mntent = NULL;
330 : }
331 :
332 0 : if (!last_mntent)
333 0 : return -1;
334 :
335 : for (;;)
336 0 : {
337 : int fd;
338 : struct mntent mnt;
339 : char buf[MAXPATHLEN * 3];
340 :
341 0 : if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
342 0 : return -1;
343 :
344 0 : if (hasmntopt (&mnt, "ro")
345 0 : || hasmntopt (&mnt, "noexec")
346 0 : || access (mnt.mnt_dir, W_OK))
347 0 : continue;
348 :
349 0 : fd = open_temp_exec_file_dir (mnt.mnt_dir);
350 :
351 0 : if (fd != -1)
352 0 : return fd;
353 : }
354 : }
355 : #endif /* HAVE_MNTENT */
356 :
357 : /* Instructions to look for a location to hold a temporary file that
358 : can be mapped in for execution. */
359 : static struct
360 : {
361 : int (*func)(const char *);
362 : const char *arg;
363 : int repeat;
364 : } open_temp_exec_file_opts[] = {
365 : { open_temp_exec_file_env, "TMPDIR", 0 },
366 : { open_temp_exec_file_dir, "/tmp", 0 },
367 : { open_temp_exec_file_dir, "/var/tmp", 0 },
368 : { open_temp_exec_file_dir, "/dev/shm", 0 },
369 : { open_temp_exec_file_env, "HOME", 0 },
370 : #ifdef HAVE_MNTENT
371 : { open_temp_exec_file_mnt, "/etc/mtab", 1 },
372 : { open_temp_exec_file_mnt, "/proc/mounts", 1 },
373 : #endif /* HAVE_MNTENT */
374 : };
375 :
376 : /* Current index into open_temp_exec_file_opts. */
377 : static int open_temp_exec_file_opts_idx = 0;
378 :
379 : /* Reset a current multi-call func, then advances to the next entry.
380 : If we're at the last, go back to the first and return nonzero,
381 : otherwise return zero. */
382 : static int
383 0 : open_temp_exec_file_opts_next (void)
384 : {
385 0 : if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
386 0 : open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
387 :
388 0 : open_temp_exec_file_opts_idx++;
389 0 : if (open_temp_exec_file_opts_idx
390 0 : == (sizeof (open_temp_exec_file_opts)
391 : / sizeof (*open_temp_exec_file_opts)))
392 : {
393 0 : open_temp_exec_file_opts_idx = 0;
394 0 : return 1;
395 : }
396 :
397 0 : return 0;
398 : }
399 :
400 : /* Return a file descriptor of a temporary zero-sized file in a
401 : writable and executable filesystem. */
402 : static int
403 0 : open_temp_exec_file (void)
404 : {
405 : int fd;
406 :
407 : do
408 : {
409 0 : fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
410 : (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
411 :
412 0 : if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
413 0 : || fd == -1)
414 : {
415 0 : if (open_temp_exec_file_opts_next ())
416 0 : break;
417 : }
418 : }
419 0 : while (fd == -1);
420 :
421 0 : return fd;
422 : }
423 :
424 : /* Map in a chunk of memory from the temporary exec file into separate
425 : locations in the virtual memory address space, one writable and one
426 : executable. Returns the address of the writable portion, after
427 : storing an offset to the corresponding executable portion at the
428 : last word of the requested chunk. */
429 : static void *
430 0 : dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
431 : {
432 : void *ptr;
433 :
434 0 : if (execfd == -1)
435 : {
436 0 : open_temp_exec_file_opts_idx = 0;
437 : retry_open:
438 0 : execfd = open_temp_exec_file ();
439 0 : if (execfd == -1)
440 0 : return MFAIL;
441 : }
442 :
443 0 : offset = execsize;
444 :
445 0 : if (ftruncate (execfd, offset + length))
446 0 : return MFAIL;
447 :
448 0 : flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
449 0 : flags |= MAP_SHARED;
450 :
451 0 : ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
452 : flags, execfd, offset);
453 0 : if (ptr == MFAIL)
454 : {
455 0 : if (!offset)
456 : {
457 0 : close (execfd);
458 0 : goto retry_open;
459 : }
460 0 : ftruncate (execfd, offset);
461 0 : return MFAIL;
462 : }
463 0 : else if (!offset
464 0 : && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
465 0 : open_temp_exec_file_opts_next ();
466 :
467 0 : start = mmap (start, length, prot, flags, execfd, offset);
468 :
469 0 : if (start == MFAIL)
470 : {
471 0 : munmap (ptr, length);
472 0 : ftruncate (execfd, offset);
473 0 : return start;
474 : }
475 :
476 0 : mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
477 :
478 0 : execsize += length;
479 :
480 0 : return start;
481 : }
482 :
483 : /* Map in a writable and executable chunk of memory if possible.
484 : Failing that, fall back to dlmmap_locked. */
485 : static void *
486 0 : dlmmap (void *start, size_t length, int prot,
487 : int flags, int fd, off_t offset)
488 : {
489 : void *ptr;
490 :
491 0 : assert (start == NULL && length % malloc_getpagesize == 0
492 : && prot == (PROT_READ | PROT_WRITE)
493 : && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
494 : && fd == -1 && offset == 0);
495 :
496 : #if FFI_CLOSURE_TEST
497 : printf ("mapping in %zi\n", length);
498 : #endif
499 :
500 : if (execfd == -1 && is_emutramp_enabled ())
501 : {
502 : ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
503 : return ptr;
504 : }
505 :
506 0 : if (execfd == -1 && !is_selinux_enabled ())
507 : {
508 0 : ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
509 :
510 0 : if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
511 : /* Cool, no need to mess with separate segments. */
512 0 : return ptr;
513 :
514 : /* If MREMAP_DUP is ever introduced and implemented, try mmap
515 : with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
516 : MREMAP_DUP and prot at this point. */
517 : }
518 :
519 0 : if (execsize == 0 || execfd == -1)
520 : {
521 0 : pthread_mutex_lock (&open_temp_exec_file_mutex);
522 0 : ptr = dlmmap_locked (start, length, prot, flags, offset);
523 0 : pthread_mutex_unlock (&open_temp_exec_file_mutex);
524 :
525 0 : return ptr;
526 : }
527 :
528 0 : return dlmmap_locked (start, length, prot, flags, offset);
529 : }
530 :
531 : /* Release memory at the given address, as well as the corresponding
532 : executable page if it's separate. */
533 : static int
534 0 : dlmunmap (void *start, size_t length)
535 : {
536 : /* We don't bother decreasing execsize or truncating the file, since
537 : we can't quite tell whether we're unmapping the end of the file.
538 : We don't expect frequent deallocation anyway. If we did, we
539 : could locate pages in the file by writing to the pages being
540 : deallocated and checking that the file contents change.
541 : Yuck. */
542 0 : msegmentptr seg = segment_holding (gm, start);
543 : void *code;
544 :
545 : #if FFI_CLOSURE_TEST
546 : printf ("unmapping %zi\n", length);
547 : #endif
548 :
549 0 : if (seg && (code = add_segment_exec_offset (start, seg)) != start)
550 : {
551 0 : int ret = munmap (code, length);
552 0 : if (ret)
553 0 : return ret;
554 : }
555 :
556 0 : return munmap (start, length);
557 : }
558 :
559 : #if FFI_CLOSURE_FREE_CODE
560 : /* Return segment holding given code address. */
561 : static msegmentptr
562 : segment_holding_code (mstate m, char* addr)
563 : {
564 : msegmentptr sp = &m->seg;
565 : for (;;) {
566 : if (addr >= add_segment_exec_offset (sp->base, sp)
567 : && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
568 : return sp;
569 : if ((sp = sp->next) == 0)
570 : return 0;
571 : }
572 : }
573 : #endif
574 :
575 : #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
576 :
577 : /* Allocate a chunk of memory with the given size. Returns a pointer
578 : to the writable address, and sets *CODE to the executable
579 : corresponding virtual address. */
580 : void *
581 0 : ffi_closure_alloc (size_t size, void **code)
582 : {
583 : void *ptr;
584 :
585 0 : if (!code)
586 0 : return NULL;
587 :
588 0 : ptr = dlmalloc (size);
589 :
590 0 : if (ptr)
591 : {
592 0 : msegmentptr seg = segment_holding (gm, ptr);
593 :
594 0 : *code = add_segment_exec_offset (ptr, seg);
595 : }
596 :
597 0 : return ptr;
598 : }
599 :
600 : /* Release a chunk of memory allocated with ffi_closure_alloc. If
601 : FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
602 : writable or the executable address given. Otherwise, only the
603 : writable address can be provided here. */
604 : void
605 0 : ffi_closure_free (void *ptr)
606 : {
607 : #if FFI_CLOSURE_FREE_CODE
608 : msegmentptr seg = segment_holding_code (gm, ptr);
609 :
610 : if (seg)
611 : ptr = sub_segment_exec_offset (ptr, seg);
612 : #endif
613 :
614 0 : dlfree (ptr);
615 0 : }
616 :
617 :
618 : #if FFI_CLOSURE_TEST
619 : /* Do some internal sanity testing to make sure allocation and
620 : deallocation of pages are working as intended. */
621 : int main ()
622 : {
623 : void *p[3];
624 : #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
625 : #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
626 : GET (0, malloc_getpagesize / 2);
627 : GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
628 : PUT (1);
629 : GET (1, 2 * malloc_getpagesize);
630 : GET (2, malloc_getpagesize / 2);
631 : PUT (1);
632 : PUT (0);
633 : PUT (2);
634 : return 0;
635 : }
636 : #endif /* FFI_CLOSURE_TEST */
637 : # else /* ! FFI_MMAP_EXEC_WRIT */
638 :
639 : /* On many systems, memory returned by malloc is writable and
640 : executable, so just use it. */
641 :
642 : #include <stdlib.h>
643 :
644 : void *
645 : ffi_closure_alloc (size_t size, void **code)
646 : {
647 : if (!code)
648 : return NULL;
649 :
650 : return *code = malloc (size);
651 : }
652 :
653 : void
654 : ffi_closure_free (void *ptr)
655 : {
656 : free (ptr);
657 : }
658 :
659 : # endif /* ! FFI_MMAP_EXEC_WRIT */
660 : #endif /* FFI_CLOSURES */
|