111
|
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 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
|
|
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 open_temp_exec_file_name (char *name, int flags)
|
|
269 {
|
|
270 int fd;
|
|
271
|
|
272 #ifdef HAVE_MKOSTEMP
|
|
273 fd = mkostemp (name, flags);
|
|
274 #else
|
|
275 fd = mkstemp (name);
|
|
276 #endif
|
|
277
|
|
278 if (fd != -1)
|
|
279 unlink (name);
|
|
280
|
|
281 return fd;
|
|
282 }
|
|
283
|
|
284 /* Open a temporary file in the named directory. */
|
|
285 static int
|
|
286 open_temp_exec_file_dir (const char *dir)
|
|
287 {
|
|
288 static const char suffix[] = "/ffiXXXXXX";
|
|
289 int lendir, flags;
|
|
290 char *tempname;
|
|
291 #ifdef O_TMPFILE
|
|
292 int fd;
|
|
293 #endif
|
|
294
|
|
295 #ifdef O_CLOEXEC
|
|
296 flags = O_CLOEXEC;
|
|
297 #else
|
|
298 flags = 0;
|
|
299 #endif
|
|
300
|
|
301 #ifdef O_TMPFILE
|
|
302 fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
|
|
303 /* If the running system does not support the O_TMPFILE flag then retry without it. */
|
|
304 if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
|
|
305 return fd;
|
|
306 } else {
|
|
307 errno = 0;
|
|
308 }
|
|
309 #endif
|
|
310
|
|
311 lendir = strlen (dir);
|
|
312 tempname = __builtin_alloca (lendir + sizeof (suffix));
|
|
313
|
|
314 if (!tempname)
|
|
315 return -1;
|
|
316
|
|
317 memcpy (tempname, dir, lendir);
|
|
318 memcpy (tempname + lendir, suffix, sizeof (suffix));
|
|
319
|
|
320 return open_temp_exec_file_name (tempname, flags);
|
|
321 }
|
|
322
|
|
323 /* Open a temporary file in the directory in the named environment
|
|
324 variable. */
|
|
325 static int
|
|
326 open_temp_exec_file_env (const char *envvar)
|
|
327 {
|
|
328 const char *value = getenv (envvar);
|
|
329
|
|
330 if (!value)
|
|
331 return -1;
|
|
332
|
|
333 return open_temp_exec_file_dir (value);
|
|
334 }
|
|
335
|
|
336 #ifdef HAVE_MNTENT
|
|
337 /* Open a temporary file in an executable and writable mount point
|
|
338 listed in the mounts file. Subsequent calls with the same mounts
|
|
339 keep searching for mount points in the same file. Providing NULL
|
|
340 as the mounts file closes the file. */
|
|
341 static int
|
|
342 open_temp_exec_file_mnt (const char *mounts)
|
|
343 {
|
|
344 static const char *last_mounts;
|
|
345 static FILE *last_mntent;
|
|
346
|
|
347 if (mounts != last_mounts)
|
|
348 {
|
|
349 if (last_mntent)
|
|
350 endmntent (last_mntent);
|
|
351
|
|
352 last_mounts = mounts;
|
|
353
|
|
354 if (mounts)
|
|
355 last_mntent = setmntent (mounts, "r");
|
|
356 else
|
|
357 last_mntent = NULL;
|
|
358 }
|
|
359
|
|
360 if (!last_mntent)
|
|
361 return -1;
|
|
362
|
|
363 for (;;)
|
|
364 {
|
|
365 int fd;
|
|
366 struct mntent mnt;
|
|
367 char buf[MAXPATHLEN * 3];
|
|
368
|
|
369 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
|
|
370 return -1;
|
|
371
|
|
372 if (hasmntopt (&mnt, "ro")
|
|
373 || hasmntopt (&mnt, "noexec")
|
|
374 || access (mnt.mnt_dir, W_OK))
|
|
375 continue;
|
|
376
|
|
377 fd = open_temp_exec_file_dir (mnt.mnt_dir);
|
|
378
|
|
379 if (fd != -1)
|
|
380 return fd;
|
|
381 }
|
|
382 }
|
|
383 #endif /* HAVE_MNTENT */
|
|
384
|
|
385 /* Instructions to look for a location to hold a temporary file that
|
|
386 can be mapped in for execution. */
|
|
387 static struct
|
|
388 {
|
|
389 int (*func)(const char *);
|
|
390 const char *arg;
|
|
391 int repeat;
|
|
392 } open_temp_exec_file_opts[] = {
|
|
393 { open_temp_exec_file_env, "TMPDIR", 0 },
|
|
394 { open_temp_exec_file_dir, "/tmp", 0 },
|
|
395 { open_temp_exec_file_dir, "/var/tmp", 0 },
|
|
396 { open_temp_exec_file_dir, "/dev/shm", 0 },
|
|
397 { open_temp_exec_file_env, "HOME", 0 },
|
|
398 #ifdef HAVE_MNTENT
|
|
399 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
|
|
400 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
|
|
401 #endif /* HAVE_MNTENT */
|
|
402 };
|
|
403
|
|
404 /* Current index into open_temp_exec_file_opts. */
|
|
405 static int open_temp_exec_file_opts_idx = 0;
|
|
406
|
|
407 /* Reset a current multi-call func, then advances to the next entry.
|
|
408 If we're at the last, go back to the first and return nonzero,
|
|
409 otherwise return zero. */
|
|
410 static int
|
|
411 open_temp_exec_file_opts_next (void)
|
|
412 {
|
|
413 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
|
|
414 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
|
|
415
|
|
416 open_temp_exec_file_opts_idx++;
|
|
417 if (open_temp_exec_file_opts_idx
|
|
418 == (sizeof (open_temp_exec_file_opts)
|
|
419 / sizeof (*open_temp_exec_file_opts)))
|
|
420 {
|
|
421 open_temp_exec_file_opts_idx = 0;
|
|
422 return 1;
|
|
423 }
|
|
424
|
|
425 return 0;
|
|
426 }
|
|
427
|
|
428 /* Return a file descriptor of a temporary zero-sized file in a
|
|
429 writable and executable filesystem. */
|
|
430 static int
|
|
431 open_temp_exec_file (void)
|
|
432 {
|
|
433 int fd;
|
|
434
|
|
435 do
|
|
436 {
|
|
437 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
|
|
438 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
|
|
439
|
|
440 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
|
|
441 || fd == -1)
|
|
442 {
|
|
443 if (open_temp_exec_file_opts_next ())
|
|
444 break;
|
|
445 }
|
|
446 }
|
|
447 while (fd == -1);
|
|
448
|
|
449 return fd;
|
|
450 }
|
|
451
|
|
452 /* Map in a chunk of memory from the temporary exec file into separate
|
|
453 locations in the virtual memory address space, one writable and one
|
|
454 executable. Returns the address of the writable portion, after
|
|
455 storing an offset to the corresponding executable portion at the
|
|
456 last word of the requested chunk. */
|
|
457 static void *
|
|
458 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
|
|
459 {
|
|
460 void *ptr;
|
|
461
|
|
462 if (execfd == -1)
|
|
463 {
|
|
464 open_temp_exec_file_opts_idx = 0;
|
|
465 retry_open:
|
|
466 execfd = open_temp_exec_file ();
|
|
467 if (execfd == -1)
|
|
468 return MFAIL;
|
|
469 }
|
|
470
|
|
471 offset = execsize;
|
|
472
|
|
473 if (ftruncate (execfd, offset + length))
|
|
474 return MFAIL;
|
|
475
|
|
476 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
|
|
477 flags |= MAP_SHARED;
|
|
478
|
|
479 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
|
|
480 flags, execfd, offset);
|
|
481 if (ptr == MFAIL)
|
|
482 {
|
|
483 if (!offset)
|
|
484 {
|
|
485 close (execfd);
|
|
486 goto retry_open;
|
|
487 }
|
|
488 ftruncate (execfd, offset);
|
|
489 return MFAIL;
|
|
490 }
|
|
491 else if (!offset
|
|
492 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
|
|
493 open_temp_exec_file_opts_next ();
|
|
494
|
|
495 start = mmap (start, length, prot, flags, execfd, offset);
|
|
496
|
|
497 if (start == MFAIL)
|
|
498 {
|
|
499 munmap (ptr, length);
|
|
500 ftruncate (execfd, offset);
|
|
501 return start;
|
|
502 }
|
|
503
|
|
504 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
|
|
505
|
|
506 execsize += length;
|
|
507
|
|
508 return start;
|
|
509 }
|
|
510
|
|
511 /* Map in a writable and executable chunk of memory if possible.
|
|
512 Failing that, fall back to dlmmap_locked. */
|
|
513 static void *
|
|
514 dlmmap (void *start, size_t length, int prot,
|
|
515 int flags, int fd, off_t offset)
|
|
516 {
|
|
517 void *ptr;
|
|
518
|
|
519 assert (start == NULL && length % malloc_getpagesize == 0
|
|
520 && prot == (PROT_READ | PROT_WRITE)
|
|
521 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
|
|
522 && fd == -1 && offset == 0);
|
|
523
|
|
524 #if FFI_CLOSURE_TEST
|
|
525 printf ("mapping in %zi\n", length);
|
|
526 #endif
|
|
527
|
|
528 if (execfd == -1 && is_emutramp_enabled ())
|
|
529 {
|
|
530 ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
|
|
531 return ptr;
|
|
532 }
|
|
533
|
|
534 if (execfd == -1 && !is_selinux_enabled ())
|
|
535 {
|
|
536 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
|
|
537
|
|
538 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
|
|
539 /* Cool, no need to mess with separate segments. */
|
|
540 return ptr;
|
|
541
|
|
542 /* If MREMAP_DUP is ever introduced and implemented, try mmap
|
|
543 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
|
|
544 MREMAP_DUP and prot at this point. */
|
|
545 }
|
|
546
|
|
547 if (execsize == 0 || execfd == -1)
|
|
548 {
|
|
549 pthread_mutex_lock (&open_temp_exec_file_mutex);
|
|
550 ptr = dlmmap_locked (start, length, prot, flags, offset);
|
|
551 pthread_mutex_unlock (&open_temp_exec_file_mutex);
|
|
552
|
|
553 return ptr;
|
|
554 }
|
|
555
|
|
556 return dlmmap_locked (start, length, prot, flags, offset);
|
|
557 }
|
|
558
|
|
559 /* Release memory at the given address, as well as the corresponding
|
|
560 executable page if it's separate. */
|
|
561 static int
|
|
562 dlmunmap (void *start, size_t length)
|
|
563 {
|
|
564 /* We don't bother decreasing execsize or truncating the file, since
|
|
565 we can't quite tell whether we're unmapping the end of the file.
|
|
566 We don't expect frequent deallocation anyway. If we did, we
|
|
567 could locate pages in the file by writing to the pages being
|
|
568 deallocated and checking that the file contents change.
|
|
569 Yuck. */
|
|
570 msegmentptr seg = segment_holding (gm, start);
|
|
571 void *code;
|
|
572
|
|
573 #if FFI_CLOSURE_TEST
|
|
574 printf ("unmapping %zi\n", length);
|
|
575 #endif
|
|
576
|
|
577 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
|
|
578 {
|
|
579 int ret = munmap (code, length);
|
|
580 if (ret)
|
|
581 return ret;
|
|
582 }
|
|
583
|
|
584 return munmap (start, length);
|
|
585 }
|
|
586
|
|
587 #if FFI_CLOSURE_FREE_CODE
|
|
588 /* Return segment holding given code address. */
|
|
589 static msegmentptr
|
|
590 segment_holding_code (mstate m, char* addr)
|
|
591 {
|
|
592 msegmentptr sp = &m->seg;
|
|
593 for (;;) {
|
|
594 if (addr >= add_segment_exec_offset (sp->base, sp)
|
|
595 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
|
|
596 return sp;
|
|
597 if ((sp = sp->next) == 0)
|
|
598 return 0;
|
|
599 }
|
|
600 }
|
|
601 #endif
|
|
602
|
|
603 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
|
|
604
|
|
605 /* Allocate a chunk of memory with the given size. Returns a pointer
|
|
606 to the writable address, and sets *CODE to the executable
|
|
607 corresponding virtual address. */
|
|
608 void *
|
|
609 ffi_closure_alloc (size_t size, void **code)
|
|
610 {
|
|
611 void *ptr;
|
|
612
|
|
613 if (!code)
|
|
614 return NULL;
|
|
615
|
|
616 ptr = dlmalloc (size);
|
|
617
|
|
618 if (ptr)
|
|
619 {
|
|
620 msegmentptr seg = segment_holding (gm, ptr);
|
|
621
|
|
622 *code = add_segment_exec_offset (ptr, seg);
|
|
623 }
|
|
624
|
|
625 return ptr;
|
|
626 }
|
|
627
|
|
628 /* Release a chunk of memory allocated with ffi_closure_alloc. If
|
|
629 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
|
|
630 writable or the executable address given. Otherwise, only the
|
|
631 writable address can be provided here. */
|
|
632 void
|
|
633 ffi_closure_free (void *ptr)
|
|
634 {
|
|
635 #if FFI_CLOSURE_FREE_CODE
|
|
636 msegmentptr seg = segment_holding_code (gm, ptr);
|
|
637
|
|
638 if (seg)
|
|
639 ptr = sub_segment_exec_offset (ptr, seg);
|
|
640 #endif
|
|
641
|
|
642 dlfree (ptr);
|
|
643 }
|
|
644
|
|
645
|
|
646 #if FFI_CLOSURE_TEST
|
|
647 /* Do some internal sanity testing to make sure allocation and
|
|
648 deallocation of pages are working as intended. */
|
|
649 int main ()
|
|
650 {
|
|
651 void *p[3];
|
|
652 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
|
|
653 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
|
|
654 GET (0, malloc_getpagesize / 2);
|
|
655 GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
|
|
656 PUT (1);
|
|
657 GET (1, 2 * malloc_getpagesize);
|
|
658 GET (2, malloc_getpagesize / 2);
|
|
659 PUT (1);
|
|
660 PUT (0);
|
|
661 PUT (2);
|
|
662 return 0;
|
|
663 }
|
|
664 #endif /* FFI_CLOSURE_TEST */
|
|
665 # else /* ! FFI_MMAP_EXEC_WRIT */
|
|
666
|
|
667 /* On many systems, memory returned by malloc is writable and
|
|
668 executable, so just use it. */
|
|
669
|
|
670 #include <stdlib.h>
|
|
671
|
|
672 void *
|
|
673 ffi_closure_alloc (size_t size, void **code)
|
|
674 {
|
|
675 if (!code)
|
|
676 return NULL;
|
|
677
|
|
678 return *code = malloc (size);
|
|
679 }
|
|
680
|
|
681 void
|
|
682 ffi_closure_free (void *ptr)
|
|
683 {
|
|
684 free (ptr);
|
|
685 }
|
|
686
|
|
687 # endif /* ! FFI_MMAP_EXEC_WRIT */
|
|
688 #endif /* FFI_CLOSURES */
|