]>
Commit | Line | Data |
---|---|---|
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 | ||
130 | static int selinux_enabled = -1; | |
131 | ||
132 | static int | |
133 | selinux_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. */ |
185 | static void *dlmalloc(size_t); | |
186 | static void dlfree(void*); | |
187 | static void *dlcalloc(size_t, size_t) MAYBE_UNUSED; | |
188 | static void *dlrealloc(void *, size_t) MAYBE_UNUSED; | |
189 | static void *dlmemalign(size_t, size_t) MAYBE_UNUSED; | |
190 | static void *dlvalloc(size_t) MAYBE_UNUSED; | |
191 | static int dlmallopt(int, int) MAYBE_UNUSED; | |
192 | static size_t dlmalloc_footprint(void) MAYBE_UNUSED; | |
193 | static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED; | |
194 | static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED; | |
195 | static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED; | |
196 | static void *dlpvalloc(size_t) MAYBE_UNUSED; | |
197 | static int dlmalloc_trim(size_t) MAYBE_UNUSED; | |
198 | static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED; | |
199 | static 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. */ |
203 | static void *dlmmap(void *, size_t, int, int, int, off_t); | |
204 | static 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. */ |
218 | static 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. */ | |
222 | static int execfd = -1; | |
223 | ||
224 | /* The amount of space already allocated from the temporary file. */ | |
225 | static size_t execsize = 0; | |
226 | ||
227 | /* Open a temporary file name, and immediately unlink it. */ | |
228 | static int | |
229 | open_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. */ | |
240 | static int | |
241 | open_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. */ | |
258 | static int | |
259 | open_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. */ | |
274 | static int | |
275 | open_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. */ | |
320 | static 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. */ | |
338 | static 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. */ | |
343 | static int | |
344 | open_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. */ | |
363 | static int | |
364 | open_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. */ | |
390 | static void * | |
391 | dlmmap_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. */ | |
446 | static void * | |
447 | dlmmap (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. */ | |
488 | static int | |
489 | dlmunmap (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. */ | |
516 | static msegmentptr | |
517 | segment_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. */ | |
535 | void * | |
536 | ffi_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. */ | |
559 | void | |
560 | ffi_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. */ | |
576 | int 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 | ||
599 | void * | |
600 | ffi_closure_alloc (size_t size, void **code) | |
601 | { | |
602 | if (!code) | |
603 | return NULL; | |
604 | ||
605 | return *code = malloc (size); | |
606 | } | |
607 | ||
608 | void | |
609 | ffi_closure_free (void *ptr) | |
610 | { | |
611 | free (ptr); | |
612 | } | |
613 | ||
614 | # endif /* ! FFI_MMAP_EXEC_WRIT */ | |
615 | #endif /* FFI_CLOSURES */ |