ยปCore Development>Code coverage>Modules/_ctypes/libffi/src/closures.c

Python code coverage for Modules/_ctypes/libffi/src/closures.c

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