1*1fd5a2e1SPrashanth Swaminathan /* -----------------------------------------------------------------------
2*1fd5a2e1SPrashanth Swaminathan closures.c - Copyright (c) 2019 Anthony Green
3*1fd5a2e1SPrashanth Swaminathan Copyright (c) 2007, 2009, 2010 Red Hat, Inc.
4*1fd5a2e1SPrashanth Swaminathan Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
5*1fd5a2e1SPrashanth Swaminathan Copyright (c) 2011 Plausible Labs Cooperative, Inc.
6*1fd5a2e1SPrashanth Swaminathan
7*1fd5a2e1SPrashanth Swaminathan Code to allocate and deallocate memory for closures.
8*1fd5a2e1SPrashanth Swaminathan
9*1fd5a2e1SPrashanth Swaminathan Permission is hereby granted, free of charge, to any person obtaining
10*1fd5a2e1SPrashanth Swaminathan a copy of this software and associated documentation files (the
11*1fd5a2e1SPrashanth Swaminathan ``Software''), to deal in the Software without restriction, including
12*1fd5a2e1SPrashanth Swaminathan without limitation the rights to use, copy, modify, merge, publish,
13*1fd5a2e1SPrashanth Swaminathan distribute, sublicense, and/or sell copies of the Software, and to
14*1fd5a2e1SPrashanth Swaminathan permit persons to whom the Software is furnished to do so, subject to
15*1fd5a2e1SPrashanth Swaminathan the following conditions:
16*1fd5a2e1SPrashanth Swaminathan
17*1fd5a2e1SPrashanth Swaminathan The above copyright notice and this permission notice shall be included
18*1fd5a2e1SPrashanth Swaminathan in all copies or substantial portions of the Software.
19*1fd5a2e1SPrashanth Swaminathan
20*1fd5a2e1SPrashanth Swaminathan THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
21*1fd5a2e1SPrashanth Swaminathan EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22*1fd5a2e1SPrashanth Swaminathan MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23*1fd5a2e1SPrashanth Swaminathan NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24*1fd5a2e1SPrashanth Swaminathan HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25*1fd5a2e1SPrashanth Swaminathan WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26*1fd5a2e1SPrashanth Swaminathan OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27*1fd5a2e1SPrashanth Swaminathan DEALINGS IN THE SOFTWARE.
28*1fd5a2e1SPrashanth Swaminathan ----------------------------------------------------------------------- */
29*1fd5a2e1SPrashanth Swaminathan
30*1fd5a2e1SPrashanth Swaminathan #if defined __linux__ && !defined _GNU_SOURCE
31*1fd5a2e1SPrashanth Swaminathan #define _GNU_SOURCE 1
32*1fd5a2e1SPrashanth Swaminathan #endif
33*1fd5a2e1SPrashanth Swaminathan
34*1fd5a2e1SPrashanth Swaminathan #include <fficonfig.h>
35*1fd5a2e1SPrashanth Swaminathan #include <ffi.h>
36*1fd5a2e1SPrashanth Swaminathan #include <ffi_common.h>
37*1fd5a2e1SPrashanth Swaminathan
38*1fd5a2e1SPrashanth Swaminathan #ifdef __NetBSD__
39*1fd5a2e1SPrashanth Swaminathan #include <sys/param.h>
40*1fd5a2e1SPrashanth Swaminathan #endif
41*1fd5a2e1SPrashanth Swaminathan
42*1fd5a2e1SPrashanth Swaminathan #if __NetBSD_Version__ - 0 >= 799007200
43*1fd5a2e1SPrashanth Swaminathan /* NetBSD with PROT_MPROTECT */
44*1fd5a2e1SPrashanth Swaminathan #include <sys/mman.h>
45*1fd5a2e1SPrashanth Swaminathan
46*1fd5a2e1SPrashanth Swaminathan #include <stddef.h>
47*1fd5a2e1SPrashanth Swaminathan #include <unistd.h>
48*1fd5a2e1SPrashanth Swaminathan
49*1fd5a2e1SPrashanth Swaminathan static const size_t overhead =
50*1fd5a2e1SPrashanth Swaminathan (sizeof(max_align_t) > sizeof(void *) + sizeof(size_t)) ?
51*1fd5a2e1SPrashanth Swaminathan sizeof(max_align_t)
52*1fd5a2e1SPrashanth Swaminathan : sizeof(void *) + sizeof(size_t);
53*1fd5a2e1SPrashanth Swaminathan
54*1fd5a2e1SPrashanth Swaminathan #define ADD_TO_POINTER(p, d) ((void *)((uintptr_t)(p) + (d)))
55*1fd5a2e1SPrashanth Swaminathan
56*1fd5a2e1SPrashanth Swaminathan void *
ffi_closure_alloc(size_t size,void ** code)57*1fd5a2e1SPrashanth Swaminathan ffi_closure_alloc (size_t size, void **code)
58*1fd5a2e1SPrashanth Swaminathan {
59*1fd5a2e1SPrashanth Swaminathan static size_t page_size;
60*1fd5a2e1SPrashanth Swaminathan size_t rounded_size;
61*1fd5a2e1SPrashanth Swaminathan void *codeseg, *dataseg;
62*1fd5a2e1SPrashanth Swaminathan int prot;
63*1fd5a2e1SPrashanth Swaminathan
64*1fd5a2e1SPrashanth Swaminathan /* Expect that PAX mprotect is active and a separate code mapping is necessary. */
65*1fd5a2e1SPrashanth Swaminathan if (!code)
66*1fd5a2e1SPrashanth Swaminathan return NULL;
67*1fd5a2e1SPrashanth Swaminathan
68*1fd5a2e1SPrashanth Swaminathan /* Obtain system page size. */
69*1fd5a2e1SPrashanth Swaminathan if (!page_size)
70*1fd5a2e1SPrashanth Swaminathan page_size = sysconf(_SC_PAGESIZE);
71*1fd5a2e1SPrashanth Swaminathan
72*1fd5a2e1SPrashanth Swaminathan /* Round allocation size up to the next page, keeping in mind the size field and pointer to code map. */
73*1fd5a2e1SPrashanth Swaminathan rounded_size = (size + overhead + page_size - 1) & ~(page_size - 1);
74*1fd5a2e1SPrashanth Swaminathan
75*1fd5a2e1SPrashanth Swaminathan /* Primary mapping is RW, but request permission to switch to PROT_EXEC later. */
76*1fd5a2e1SPrashanth Swaminathan prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
77*1fd5a2e1SPrashanth Swaminathan dataseg = mmap(NULL, rounded_size, prot, MAP_ANON | MAP_PRIVATE, -1, 0);
78*1fd5a2e1SPrashanth Swaminathan if (dataseg == MAP_FAILED)
79*1fd5a2e1SPrashanth Swaminathan return NULL;
80*1fd5a2e1SPrashanth Swaminathan
81*1fd5a2e1SPrashanth Swaminathan /* Create secondary mapping and switch it to RX. */
82*1fd5a2e1SPrashanth Swaminathan codeseg = mremap(dataseg, rounded_size, NULL, rounded_size, MAP_REMAPDUP);
83*1fd5a2e1SPrashanth Swaminathan if (codeseg == MAP_FAILED) {
84*1fd5a2e1SPrashanth Swaminathan munmap(dataseg, rounded_size);
85*1fd5a2e1SPrashanth Swaminathan return NULL;
86*1fd5a2e1SPrashanth Swaminathan }
87*1fd5a2e1SPrashanth Swaminathan if (mprotect(codeseg, rounded_size, PROT_READ | PROT_EXEC) == -1) {
88*1fd5a2e1SPrashanth Swaminathan munmap(codeseg, rounded_size);
89*1fd5a2e1SPrashanth Swaminathan munmap(dataseg, rounded_size);
90*1fd5a2e1SPrashanth Swaminathan return NULL;
91*1fd5a2e1SPrashanth Swaminathan }
92*1fd5a2e1SPrashanth Swaminathan
93*1fd5a2e1SPrashanth Swaminathan /* Remember allocation size and location of the secondary mapping for ffi_closure_free. */
94*1fd5a2e1SPrashanth Swaminathan memcpy(dataseg, &rounded_size, sizeof(rounded_size));
95*1fd5a2e1SPrashanth Swaminathan memcpy(ADD_TO_POINTER(dataseg, sizeof(size_t)), &codeseg, sizeof(void *));
96*1fd5a2e1SPrashanth Swaminathan *code = ADD_TO_POINTER(codeseg, overhead);
97*1fd5a2e1SPrashanth Swaminathan return ADD_TO_POINTER(dataseg, overhead);
98*1fd5a2e1SPrashanth Swaminathan }
99*1fd5a2e1SPrashanth Swaminathan
100*1fd5a2e1SPrashanth Swaminathan void
ffi_closure_free(void * ptr)101*1fd5a2e1SPrashanth Swaminathan ffi_closure_free (void *ptr)
102*1fd5a2e1SPrashanth Swaminathan {
103*1fd5a2e1SPrashanth Swaminathan void *codeseg, *dataseg;
104*1fd5a2e1SPrashanth Swaminathan size_t rounded_size;
105*1fd5a2e1SPrashanth Swaminathan
106*1fd5a2e1SPrashanth Swaminathan dataseg = ADD_TO_POINTER(ptr, -overhead);
107*1fd5a2e1SPrashanth Swaminathan memcpy(&rounded_size, dataseg, sizeof(rounded_size));
108*1fd5a2e1SPrashanth Swaminathan memcpy(&codeseg, ADD_TO_POINTER(dataseg, sizeof(size_t)), sizeof(void *));
109*1fd5a2e1SPrashanth Swaminathan munmap(dataseg, rounded_size);
110*1fd5a2e1SPrashanth Swaminathan munmap(codeseg, rounded_size);
111*1fd5a2e1SPrashanth Swaminathan }
112*1fd5a2e1SPrashanth Swaminathan #else /* !NetBSD with PROT_MPROTECT */
113*1fd5a2e1SPrashanth Swaminathan
114*1fd5a2e1SPrashanth Swaminathan #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
115*1fd5a2e1SPrashanth Swaminathan # if __linux__ && !defined(__ANDROID__)
116*1fd5a2e1SPrashanth Swaminathan /* This macro indicates it may be forbidden to map anonymous memory
117*1fd5a2e1SPrashanth Swaminathan with both write and execute permission. Code compiled when this
118*1fd5a2e1SPrashanth Swaminathan option is defined will attempt to map such pages once, but if it
119*1fd5a2e1SPrashanth Swaminathan fails, it falls back to creating a temporary file in a writable and
120*1fd5a2e1SPrashanth Swaminathan executable filesystem and mapping pages from it into separate
121*1fd5a2e1SPrashanth Swaminathan locations in the virtual memory space, one location writable and
122*1fd5a2e1SPrashanth Swaminathan another executable. */
123*1fd5a2e1SPrashanth Swaminathan # define FFI_MMAP_EXEC_WRIT 1
124*1fd5a2e1SPrashanth Swaminathan # define HAVE_MNTENT 1
125*1fd5a2e1SPrashanth Swaminathan # endif
126*1fd5a2e1SPrashanth Swaminathan # if defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)
127*1fd5a2e1SPrashanth Swaminathan /* Windows systems may have Data Execution Protection (DEP) enabled,
128*1fd5a2e1SPrashanth Swaminathan which requires the use of VirtualMalloc/VirtualFree to alloc/free
129*1fd5a2e1SPrashanth Swaminathan executable memory. */
130*1fd5a2e1SPrashanth Swaminathan # define FFI_MMAP_EXEC_WRIT 1
131*1fd5a2e1SPrashanth Swaminathan # endif
132*1fd5a2e1SPrashanth Swaminathan #endif
133*1fd5a2e1SPrashanth Swaminathan
134*1fd5a2e1SPrashanth Swaminathan #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
135*1fd5a2e1SPrashanth Swaminathan # if defined(__linux__) && !defined(__ANDROID__)
136*1fd5a2e1SPrashanth Swaminathan /* When defined to 1 check for SELinux and if SELinux is active,
137*1fd5a2e1SPrashanth Swaminathan don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
138*1fd5a2e1SPrashanth Swaminathan might cause audit messages. */
139*1fd5a2e1SPrashanth Swaminathan # define FFI_MMAP_EXEC_SELINUX 1
140*1fd5a2e1SPrashanth Swaminathan # endif
141*1fd5a2e1SPrashanth Swaminathan #endif
142*1fd5a2e1SPrashanth Swaminathan
143*1fd5a2e1SPrashanth Swaminathan #if FFI_CLOSURES
144*1fd5a2e1SPrashanth Swaminathan
145*1fd5a2e1SPrashanth Swaminathan #if FFI_EXEC_TRAMPOLINE_TABLE
146*1fd5a2e1SPrashanth Swaminathan
147*1fd5a2e1SPrashanth Swaminathan #ifdef __MACH__
148*1fd5a2e1SPrashanth Swaminathan
149*1fd5a2e1SPrashanth Swaminathan #include <mach/mach.h>
150*1fd5a2e1SPrashanth Swaminathan #include <pthread.h>
151*1fd5a2e1SPrashanth Swaminathan #include <stdio.h>
152*1fd5a2e1SPrashanth Swaminathan #include <stdlib.h>
153*1fd5a2e1SPrashanth Swaminathan
154*1fd5a2e1SPrashanth Swaminathan extern void *ffi_closure_trampoline_table_page;
155*1fd5a2e1SPrashanth Swaminathan
156*1fd5a2e1SPrashanth Swaminathan typedef struct ffi_trampoline_table ffi_trampoline_table;
157*1fd5a2e1SPrashanth Swaminathan typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
158*1fd5a2e1SPrashanth Swaminathan
159*1fd5a2e1SPrashanth Swaminathan struct ffi_trampoline_table
160*1fd5a2e1SPrashanth Swaminathan {
161*1fd5a2e1SPrashanth Swaminathan /* contiguous writable and executable pages */
162*1fd5a2e1SPrashanth Swaminathan vm_address_t config_page;
163*1fd5a2e1SPrashanth Swaminathan vm_address_t trampoline_page;
164*1fd5a2e1SPrashanth Swaminathan
165*1fd5a2e1SPrashanth Swaminathan /* free list tracking */
166*1fd5a2e1SPrashanth Swaminathan uint16_t free_count;
167*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_entry *free_list;
168*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_entry *free_list_pool;
169*1fd5a2e1SPrashanth Swaminathan
170*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table *prev;
171*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table *next;
172*1fd5a2e1SPrashanth Swaminathan };
173*1fd5a2e1SPrashanth Swaminathan
174*1fd5a2e1SPrashanth Swaminathan struct ffi_trampoline_table_entry
175*1fd5a2e1SPrashanth Swaminathan {
176*1fd5a2e1SPrashanth Swaminathan void *(*trampoline) (void);
177*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_entry *next;
178*1fd5a2e1SPrashanth Swaminathan };
179*1fd5a2e1SPrashanth Swaminathan
180*1fd5a2e1SPrashanth Swaminathan /* Total number of trampolines that fit in one trampoline table */
181*1fd5a2e1SPrashanth Swaminathan #define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
182*1fd5a2e1SPrashanth Swaminathan
183*1fd5a2e1SPrashanth Swaminathan static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
184*1fd5a2e1SPrashanth Swaminathan static ffi_trampoline_table *ffi_trampoline_tables = NULL;
185*1fd5a2e1SPrashanth Swaminathan
186*1fd5a2e1SPrashanth Swaminathan static ffi_trampoline_table *
ffi_trampoline_table_alloc(void)187*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_alloc (void)
188*1fd5a2e1SPrashanth Swaminathan {
189*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table *table;
190*1fd5a2e1SPrashanth Swaminathan vm_address_t config_page;
191*1fd5a2e1SPrashanth Swaminathan vm_address_t trampoline_page;
192*1fd5a2e1SPrashanth Swaminathan vm_address_t trampoline_page_template;
193*1fd5a2e1SPrashanth Swaminathan vm_prot_t cur_prot;
194*1fd5a2e1SPrashanth Swaminathan vm_prot_t max_prot;
195*1fd5a2e1SPrashanth Swaminathan kern_return_t kt;
196*1fd5a2e1SPrashanth Swaminathan uint16_t i;
197*1fd5a2e1SPrashanth Swaminathan
198*1fd5a2e1SPrashanth Swaminathan /* Allocate two pages -- a config page and a placeholder page */
199*1fd5a2e1SPrashanth Swaminathan config_page = 0x0;
200*1fd5a2e1SPrashanth Swaminathan kt = vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
201*1fd5a2e1SPrashanth Swaminathan VM_FLAGS_ANYWHERE);
202*1fd5a2e1SPrashanth Swaminathan if (kt != KERN_SUCCESS)
203*1fd5a2e1SPrashanth Swaminathan return NULL;
204*1fd5a2e1SPrashanth Swaminathan
205*1fd5a2e1SPrashanth Swaminathan /* Remap the trampoline table on top of the placeholder page */
206*1fd5a2e1SPrashanth Swaminathan trampoline_page = config_page + PAGE_MAX_SIZE;
207*1fd5a2e1SPrashanth Swaminathan trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
208*1fd5a2e1SPrashanth Swaminathan #ifdef __arm__
209*1fd5a2e1SPrashanth Swaminathan /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
210*1fd5a2e1SPrashanth Swaminathan trampoline_page_template &= ~1UL;
211*1fd5a2e1SPrashanth Swaminathan #endif
212*1fd5a2e1SPrashanth Swaminathan kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0,
213*1fd5a2e1SPrashanth Swaminathan VM_FLAGS_OVERWRITE, mach_task_self (), trampoline_page_template,
214*1fd5a2e1SPrashanth Swaminathan FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
215*1fd5a2e1SPrashanth Swaminathan if (kt != KERN_SUCCESS)
216*1fd5a2e1SPrashanth Swaminathan {
217*1fd5a2e1SPrashanth Swaminathan vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
218*1fd5a2e1SPrashanth Swaminathan return NULL;
219*1fd5a2e1SPrashanth Swaminathan }
220*1fd5a2e1SPrashanth Swaminathan
221*1fd5a2e1SPrashanth Swaminathan /* We have valid trampoline and config pages */
222*1fd5a2e1SPrashanth Swaminathan table = calloc (1, sizeof (ffi_trampoline_table));
223*1fd5a2e1SPrashanth Swaminathan table->free_count = FFI_TRAMPOLINE_COUNT;
224*1fd5a2e1SPrashanth Swaminathan table->config_page = config_page;
225*1fd5a2e1SPrashanth Swaminathan table->trampoline_page = trampoline_page;
226*1fd5a2e1SPrashanth Swaminathan
227*1fd5a2e1SPrashanth Swaminathan /* Create and initialize the free list */
228*1fd5a2e1SPrashanth Swaminathan table->free_list_pool =
229*1fd5a2e1SPrashanth Swaminathan calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
230*1fd5a2e1SPrashanth Swaminathan
231*1fd5a2e1SPrashanth Swaminathan for (i = 0; i < table->free_count; i++)
232*1fd5a2e1SPrashanth Swaminathan {
233*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
234*1fd5a2e1SPrashanth Swaminathan entry->trampoline =
235*1fd5a2e1SPrashanth Swaminathan (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
236*1fd5a2e1SPrashanth Swaminathan
237*1fd5a2e1SPrashanth Swaminathan if (i < table->free_count - 1)
238*1fd5a2e1SPrashanth Swaminathan entry->next = &table->free_list_pool[i + 1];
239*1fd5a2e1SPrashanth Swaminathan }
240*1fd5a2e1SPrashanth Swaminathan
241*1fd5a2e1SPrashanth Swaminathan table->free_list = table->free_list_pool;
242*1fd5a2e1SPrashanth Swaminathan
243*1fd5a2e1SPrashanth Swaminathan return table;
244*1fd5a2e1SPrashanth Swaminathan }
245*1fd5a2e1SPrashanth Swaminathan
246*1fd5a2e1SPrashanth Swaminathan static void
ffi_trampoline_table_free(ffi_trampoline_table * table)247*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_free (ffi_trampoline_table *table)
248*1fd5a2e1SPrashanth Swaminathan {
249*1fd5a2e1SPrashanth Swaminathan /* Remove from the list */
250*1fd5a2e1SPrashanth Swaminathan if (table->prev != NULL)
251*1fd5a2e1SPrashanth Swaminathan table->prev->next = table->next;
252*1fd5a2e1SPrashanth Swaminathan
253*1fd5a2e1SPrashanth Swaminathan if (table->next != NULL)
254*1fd5a2e1SPrashanth Swaminathan table->next->prev = table->prev;
255*1fd5a2e1SPrashanth Swaminathan
256*1fd5a2e1SPrashanth Swaminathan /* Deallocate pages */
257*1fd5a2e1SPrashanth Swaminathan vm_deallocate (mach_task_self (), table->config_page, PAGE_MAX_SIZE * 2);
258*1fd5a2e1SPrashanth Swaminathan
259*1fd5a2e1SPrashanth Swaminathan /* Deallocate free list */
260*1fd5a2e1SPrashanth Swaminathan free (table->free_list_pool);
261*1fd5a2e1SPrashanth Swaminathan free (table);
262*1fd5a2e1SPrashanth Swaminathan }
263*1fd5a2e1SPrashanth Swaminathan
264*1fd5a2e1SPrashanth Swaminathan void *
ffi_closure_alloc(size_t size,void ** code)265*1fd5a2e1SPrashanth Swaminathan ffi_closure_alloc (size_t size, void **code)
266*1fd5a2e1SPrashanth Swaminathan {
267*1fd5a2e1SPrashanth Swaminathan /* Create the closure */
268*1fd5a2e1SPrashanth Swaminathan ffi_closure *closure = malloc (size);
269*1fd5a2e1SPrashanth Swaminathan if (closure == NULL)
270*1fd5a2e1SPrashanth Swaminathan return NULL;
271*1fd5a2e1SPrashanth Swaminathan
272*1fd5a2e1SPrashanth Swaminathan pthread_mutex_lock (&ffi_trampoline_lock);
273*1fd5a2e1SPrashanth Swaminathan
274*1fd5a2e1SPrashanth Swaminathan /* Check for an active trampoline table with available entries. */
275*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table *table = ffi_trampoline_tables;
276*1fd5a2e1SPrashanth Swaminathan if (table == NULL || table->free_list == NULL)
277*1fd5a2e1SPrashanth Swaminathan {
278*1fd5a2e1SPrashanth Swaminathan table = ffi_trampoline_table_alloc ();
279*1fd5a2e1SPrashanth Swaminathan if (table == NULL)
280*1fd5a2e1SPrashanth Swaminathan {
281*1fd5a2e1SPrashanth Swaminathan pthread_mutex_unlock (&ffi_trampoline_lock);
282*1fd5a2e1SPrashanth Swaminathan free (closure);
283*1fd5a2e1SPrashanth Swaminathan return NULL;
284*1fd5a2e1SPrashanth Swaminathan }
285*1fd5a2e1SPrashanth Swaminathan
286*1fd5a2e1SPrashanth Swaminathan /* Insert the new table at the top of the list */
287*1fd5a2e1SPrashanth Swaminathan table->next = ffi_trampoline_tables;
288*1fd5a2e1SPrashanth Swaminathan if (table->next != NULL)
289*1fd5a2e1SPrashanth Swaminathan table->next->prev = table;
290*1fd5a2e1SPrashanth Swaminathan
291*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_tables = table;
292*1fd5a2e1SPrashanth Swaminathan }
293*1fd5a2e1SPrashanth Swaminathan
294*1fd5a2e1SPrashanth Swaminathan /* Claim the free entry */
295*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
296*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_tables->free_list = entry->next;
297*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_tables->free_count--;
298*1fd5a2e1SPrashanth Swaminathan entry->next = NULL;
299*1fd5a2e1SPrashanth Swaminathan
300*1fd5a2e1SPrashanth Swaminathan pthread_mutex_unlock (&ffi_trampoline_lock);
301*1fd5a2e1SPrashanth Swaminathan
302*1fd5a2e1SPrashanth Swaminathan /* Initialize the return values */
303*1fd5a2e1SPrashanth Swaminathan *code = entry->trampoline;
304*1fd5a2e1SPrashanth Swaminathan closure->trampoline_table = table;
305*1fd5a2e1SPrashanth Swaminathan closure->trampoline_table_entry = entry;
306*1fd5a2e1SPrashanth Swaminathan
307*1fd5a2e1SPrashanth Swaminathan return closure;
308*1fd5a2e1SPrashanth Swaminathan }
309*1fd5a2e1SPrashanth Swaminathan
310*1fd5a2e1SPrashanth Swaminathan void
ffi_closure_free(void * ptr)311*1fd5a2e1SPrashanth Swaminathan ffi_closure_free (void *ptr)
312*1fd5a2e1SPrashanth Swaminathan {
313*1fd5a2e1SPrashanth Swaminathan ffi_closure *closure = ptr;
314*1fd5a2e1SPrashanth Swaminathan
315*1fd5a2e1SPrashanth Swaminathan pthread_mutex_lock (&ffi_trampoline_lock);
316*1fd5a2e1SPrashanth Swaminathan
317*1fd5a2e1SPrashanth Swaminathan /* Fetch the table and entry references */
318*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table *table = closure->trampoline_table;
319*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
320*1fd5a2e1SPrashanth Swaminathan
321*1fd5a2e1SPrashanth Swaminathan /* Return the entry to the free list */
322*1fd5a2e1SPrashanth Swaminathan entry->next = table->free_list;
323*1fd5a2e1SPrashanth Swaminathan table->free_list = entry;
324*1fd5a2e1SPrashanth Swaminathan table->free_count++;
325*1fd5a2e1SPrashanth Swaminathan
326*1fd5a2e1SPrashanth Swaminathan /* If all trampolines within this table are free, and at least one other table exists, deallocate
327*1fd5a2e1SPrashanth Swaminathan * the table */
328*1fd5a2e1SPrashanth Swaminathan if (table->free_count == FFI_TRAMPOLINE_COUNT
329*1fd5a2e1SPrashanth Swaminathan && ffi_trampoline_tables != table)
330*1fd5a2e1SPrashanth Swaminathan {
331*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_table_free (table);
332*1fd5a2e1SPrashanth Swaminathan }
333*1fd5a2e1SPrashanth Swaminathan else if (ffi_trampoline_tables != table)
334*1fd5a2e1SPrashanth Swaminathan {
335*1fd5a2e1SPrashanth Swaminathan /* Otherwise, bump this table to the top of the list */
336*1fd5a2e1SPrashanth Swaminathan table->prev = NULL;
337*1fd5a2e1SPrashanth Swaminathan table->next = ffi_trampoline_tables;
338*1fd5a2e1SPrashanth Swaminathan if (ffi_trampoline_tables != NULL)
339*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_tables->prev = table;
340*1fd5a2e1SPrashanth Swaminathan
341*1fd5a2e1SPrashanth Swaminathan ffi_trampoline_tables = table;
342*1fd5a2e1SPrashanth Swaminathan }
343*1fd5a2e1SPrashanth Swaminathan
344*1fd5a2e1SPrashanth Swaminathan pthread_mutex_unlock (&ffi_trampoline_lock);
345*1fd5a2e1SPrashanth Swaminathan
346*1fd5a2e1SPrashanth Swaminathan /* Free the closure */
347*1fd5a2e1SPrashanth Swaminathan free (closure);
348*1fd5a2e1SPrashanth Swaminathan }
349*1fd5a2e1SPrashanth Swaminathan
350*1fd5a2e1SPrashanth Swaminathan #endif
351*1fd5a2e1SPrashanth Swaminathan
352*1fd5a2e1SPrashanth Swaminathan // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
353*1fd5a2e1SPrashanth Swaminathan
354*1fd5a2e1SPrashanth Swaminathan #elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
355*1fd5a2e1SPrashanth Swaminathan
356*1fd5a2e1SPrashanth Swaminathan #define USE_LOCKS 1
357*1fd5a2e1SPrashanth Swaminathan #define USE_DL_PREFIX 1
358*1fd5a2e1SPrashanth Swaminathan #ifdef __GNUC__
359*1fd5a2e1SPrashanth Swaminathan #ifndef USE_BUILTIN_FFS
360*1fd5a2e1SPrashanth Swaminathan #define USE_BUILTIN_FFS 1
361*1fd5a2e1SPrashanth Swaminathan #endif
362*1fd5a2e1SPrashanth Swaminathan #endif
363*1fd5a2e1SPrashanth Swaminathan
364*1fd5a2e1SPrashanth Swaminathan /* We need to use mmap, not sbrk. */
365*1fd5a2e1SPrashanth Swaminathan #define HAVE_MORECORE 0
366*1fd5a2e1SPrashanth Swaminathan
367*1fd5a2e1SPrashanth Swaminathan /* We could, in theory, support mremap, but it wouldn't buy us anything. */
368*1fd5a2e1SPrashanth Swaminathan #define HAVE_MREMAP 0
369*1fd5a2e1SPrashanth Swaminathan
370*1fd5a2e1SPrashanth Swaminathan /* We have no use for this, so save some code and data. */
371*1fd5a2e1SPrashanth Swaminathan #define NO_MALLINFO 1
372*1fd5a2e1SPrashanth Swaminathan
373*1fd5a2e1SPrashanth Swaminathan /* We need all allocations to be in regular segments, otherwise we
374*1fd5a2e1SPrashanth Swaminathan lose track of the corresponding code address. */
375*1fd5a2e1SPrashanth Swaminathan #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
376*1fd5a2e1SPrashanth Swaminathan
377*1fd5a2e1SPrashanth Swaminathan /* Don't allocate more than a page unless needed. */
378*1fd5a2e1SPrashanth Swaminathan #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
379*1fd5a2e1SPrashanth Swaminathan
380*1fd5a2e1SPrashanth Swaminathan #include <sys/types.h>
381*1fd5a2e1SPrashanth Swaminathan #include <sys/stat.h>
382*1fd5a2e1SPrashanth Swaminathan #include <fcntl.h>
383*1fd5a2e1SPrashanth Swaminathan #include <errno.h>
384*1fd5a2e1SPrashanth Swaminathan #ifndef _MSC_VER
385*1fd5a2e1SPrashanth Swaminathan #include <unistd.h>
386*1fd5a2e1SPrashanth Swaminathan #endif
387*1fd5a2e1SPrashanth Swaminathan #include <string.h>
388*1fd5a2e1SPrashanth Swaminathan #include <stdio.h>
389*1fd5a2e1SPrashanth Swaminathan #if !defined(X86_WIN32) && !defined(X86_WIN64) && !defined(_M_ARM64)
390*1fd5a2e1SPrashanth Swaminathan #ifdef HAVE_MNTENT
391*1fd5a2e1SPrashanth Swaminathan #include <mntent.h>
392*1fd5a2e1SPrashanth Swaminathan #endif /* HAVE_MNTENT */
393*1fd5a2e1SPrashanth Swaminathan #include <sys/param.h>
394*1fd5a2e1SPrashanth Swaminathan #include <pthread.h>
395*1fd5a2e1SPrashanth Swaminathan
396*1fd5a2e1SPrashanth Swaminathan /* We don't want sys/mman.h to be included after we redefine mmap and
397*1fd5a2e1SPrashanth Swaminathan dlmunmap. */
398*1fd5a2e1SPrashanth Swaminathan #include <sys/mman.h>
399*1fd5a2e1SPrashanth Swaminathan #define LACKS_SYS_MMAN_H 1
400*1fd5a2e1SPrashanth Swaminathan
401*1fd5a2e1SPrashanth Swaminathan #if FFI_MMAP_EXEC_SELINUX
402*1fd5a2e1SPrashanth Swaminathan #include <sys/statfs.h>
403*1fd5a2e1SPrashanth Swaminathan #include <stdlib.h>
404*1fd5a2e1SPrashanth Swaminathan
405*1fd5a2e1SPrashanth Swaminathan static int selinux_enabled = -1;
406*1fd5a2e1SPrashanth Swaminathan
407*1fd5a2e1SPrashanth Swaminathan static int
selinux_enabled_check(void)408*1fd5a2e1SPrashanth Swaminathan selinux_enabled_check (void)
409*1fd5a2e1SPrashanth Swaminathan {
410*1fd5a2e1SPrashanth Swaminathan struct statfs sfs;
411*1fd5a2e1SPrashanth Swaminathan FILE *f;
412*1fd5a2e1SPrashanth Swaminathan char *buf = NULL;
413*1fd5a2e1SPrashanth Swaminathan size_t len = 0;
414*1fd5a2e1SPrashanth Swaminathan
415*1fd5a2e1SPrashanth Swaminathan if (statfs ("/selinux", &sfs) >= 0
416*1fd5a2e1SPrashanth Swaminathan && (unsigned int) sfs.f_type == 0xf97cff8cU)
417*1fd5a2e1SPrashanth Swaminathan return 1;
418*1fd5a2e1SPrashanth Swaminathan f = fopen ("/proc/mounts", "r");
419*1fd5a2e1SPrashanth Swaminathan if (f == NULL)
420*1fd5a2e1SPrashanth Swaminathan return 0;
421*1fd5a2e1SPrashanth Swaminathan while (getline (&buf, &len, f) >= 0)
422*1fd5a2e1SPrashanth Swaminathan {
423*1fd5a2e1SPrashanth Swaminathan char *p = strchr (buf, ' ');
424*1fd5a2e1SPrashanth Swaminathan if (p == NULL)
425*1fd5a2e1SPrashanth Swaminathan break;
426*1fd5a2e1SPrashanth Swaminathan p = strchr (p + 1, ' ');
427*1fd5a2e1SPrashanth Swaminathan if (p == NULL)
428*1fd5a2e1SPrashanth Swaminathan break;
429*1fd5a2e1SPrashanth Swaminathan if (strncmp (p + 1, "selinuxfs ", 10) == 0)
430*1fd5a2e1SPrashanth Swaminathan {
431*1fd5a2e1SPrashanth Swaminathan free (buf);
432*1fd5a2e1SPrashanth Swaminathan fclose (f);
433*1fd5a2e1SPrashanth Swaminathan return 1;
434*1fd5a2e1SPrashanth Swaminathan }
435*1fd5a2e1SPrashanth Swaminathan }
436*1fd5a2e1SPrashanth Swaminathan free (buf);
437*1fd5a2e1SPrashanth Swaminathan fclose (f);
438*1fd5a2e1SPrashanth Swaminathan return 0;
439*1fd5a2e1SPrashanth Swaminathan }
440*1fd5a2e1SPrashanth Swaminathan
441*1fd5a2e1SPrashanth Swaminathan #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
442*1fd5a2e1SPrashanth Swaminathan : (selinux_enabled = selinux_enabled_check ()))
443*1fd5a2e1SPrashanth Swaminathan
444*1fd5a2e1SPrashanth Swaminathan #else
445*1fd5a2e1SPrashanth Swaminathan
446*1fd5a2e1SPrashanth Swaminathan #define is_selinux_enabled() 0
447*1fd5a2e1SPrashanth Swaminathan
448*1fd5a2e1SPrashanth Swaminathan #endif /* !FFI_MMAP_EXEC_SELINUX */
449*1fd5a2e1SPrashanth Swaminathan
450*1fd5a2e1SPrashanth Swaminathan /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
451*1fd5a2e1SPrashanth Swaminathan #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
452*1fd5a2e1SPrashanth Swaminathan #include <stdlib.h>
453*1fd5a2e1SPrashanth Swaminathan
454*1fd5a2e1SPrashanth Swaminathan static int emutramp_enabled = -1;
455*1fd5a2e1SPrashanth Swaminathan
456*1fd5a2e1SPrashanth Swaminathan static int
emutramp_enabled_check(void)457*1fd5a2e1SPrashanth Swaminathan emutramp_enabled_check (void)
458*1fd5a2e1SPrashanth Swaminathan {
459*1fd5a2e1SPrashanth Swaminathan char *buf = NULL;
460*1fd5a2e1SPrashanth Swaminathan size_t len = 0;
461*1fd5a2e1SPrashanth Swaminathan FILE *f;
462*1fd5a2e1SPrashanth Swaminathan int ret;
463*1fd5a2e1SPrashanth Swaminathan f = fopen ("/proc/self/status", "r");
464*1fd5a2e1SPrashanth Swaminathan if (f == NULL)
465*1fd5a2e1SPrashanth Swaminathan return 0;
466*1fd5a2e1SPrashanth Swaminathan ret = 0;
467*1fd5a2e1SPrashanth Swaminathan
468*1fd5a2e1SPrashanth Swaminathan while (getline (&buf, &len, f) != -1)
469*1fd5a2e1SPrashanth Swaminathan if (!strncmp (buf, "PaX:", 4))
470*1fd5a2e1SPrashanth Swaminathan {
471*1fd5a2e1SPrashanth Swaminathan char emutramp;
472*1fd5a2e1SPrashanth Swaminathan if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
473*1fd5a2e1SPrashanth Swaminathan ret = (emutramp == 'E');
474*1fd5a2e1SPrashanth Swaminathan break;
475*1fd5a2e1SPrashanth Swaminathan }
476*1fd5a2e1SPrashanth Swaminathan free (buf);
477*1fd5a2e1SPrashanth Swaminathan fclose (f);
478*1fd5a2e1SPrashanth Swaminathan return ret;
479*1fd5a2e1SPrashanth Swaminathan }
480*1fd5a2e1SPrashanth Swaminathan
481*1fd5a2e1SPrashanth Swaminathan #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
482*1fd5a2e1SPrashanth Swaminathan : (emutramp_enabled = emutramp_enabled_check ()))
483*1fd5a2e1SPrashanth Swaminathan #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
484*1fd5a2e1SPrashanth Swaminathan
485*1fd5a2e1SPrashanth Swaminathan #elif defined (__CYGWIN__) || defined(__INTERIX)
486*1fd5a2e1SPrashanth Swaminathan
487*1fd5a2e1SPrashanth Swaminathan #include <sys/mman.h>
488*1fd5a2e1SPrashanth Swaminathan
489*1fd5a2e1SPrashanth Swaminathan /* Cygwin is Linux-like, but not quite that Linux-like. */
490*1fd5a2e1SPrashanth Swaminathan #define is_selinux_enabled() 0
491*1fd5a2e1SPrashanth Swaminathan
492*1fd5a2e1SPrashanth Swaminathan #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
493*1fd5a2e1SPrashanth Swaminathan
494*1fd5a2e1SPrashanth Swaminathan #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
495*1fd5a2e1SPrashanth Swaminathan #define is_emutramp_enabled() 0
496*1fd5a2e1SPrashanth Swaminathan #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
497*1fd5a2e1SPrashanth Swaminathan
498*1fd5a2e1SPrashanth Swaminathan /* Declare all functions defined in dlmalloc.c as static. */
499*1fd5a2e1SPrashanth Swaminathan static void *dlmalloc(size_t);
500*1fd5a2e1SPrashanth Swaminathan static void dlfree(void*);
501*1fd5a2e1SPrashanth Swaminathan static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
502*1fd5a2e1SPrashanth Swaminathan static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
503*1fd5a2e1SPrashanth Swaminathan static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
504*1fd5a2e1SPrashanth Swaminathan static void *dlvalloc(size_t) MAYBE_UNUSED;
505*1fd5a2e1SPrashanth Swaminathan static int dlmallopt(int, int) MAYBE_UNUSED;
506*1fd5a2e1SPrashanth Swaminathan static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
507*1fd5a2e1SPrashanth Swaminathan static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
508*1fd5a2e1SPrashanth Swaminathan static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
509*1fd5a2e1SPrashanth Swaminathan static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
510*1fd5a2e1SPrashanth Swaminathan static void *dlpvalloc(size_t) MAYBE_UNUSED;
511*1fd5a2e1SPrashanth Swaminathan static int dlmalloc_trim(size_t) MAYBE_UNUSED;
512*1fd5a2e1SPrashanth Swaminathan static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
513*1fd5a2e1SPrashanth Swaminathan static void dlmalloc_stats(void) MAYBE_UNUSED;
514*1fd5a2e1SPrashanth Swaminathan
515*1fd5a2e1SPrashanth Swaminathan #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
516*1fd5a2e1SPrashanth Swaminathan /* Use these for mmap and munmap within dlmalloc.c. */
517*1fd5a2e1SPrashanth Swaminathan static void *dlmmap(void *, size_t, int, int, int, off_t);
518*1fd5a2e1SPrashanth Swaminathan static int dlmunmap(void *, size_t);
519*1fd5a2e1SPrashanth Swaminathan #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
520*1fd5a2e1SPrashanth Swaminathan
521*1fd5a2e1SPrashanth Swaminathan #define mmap dlmmap
522*1fd5a2e1SPrashanth Swaminathan #define munmap dlmunmap
523*1fd5a2e1SPrashanth Swaminathan
524*1fd5a2e1SPrashanth Swaminathan #include "dlmalloc.c"
525*1fd5a2e1SPrashanth Swaminathan
526*1fd5a2e1SPrashanth Swaminathan #undef mmap
527*1fd5a2e1SPrashanth Swaminathan #undef munmap
528*1fd5a2e1SPrashanth Swaminathan
529*1fd5a2e1SPrashanth Swaminathan #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
530*1fd5a2e1SPrashanth Swaminathan
531*1fd5a2e1SPrashanth Swaminathan /* A mutex used to synchronize access to *exec* variables in this file. */
532*1fd5a2e1SPrashanth Swaminathan static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
533*1fd5a2e1SPrashanth Swaminathan
534*1fd5a2e1SPrashanth Swaminathan /* A file descriptor of a temporary file from which we'll map
535*1fd5a2e1SPrashanth Swaminathan executable pages. */
536*1fd5a2e1SPrashanth Swaminathan static int execfd = -1;
537*1fd5a2e1SPrashanth Swaminathan
538*1fd5a2e1SPrashanth Swaminathan /* The amount of space already allocated from the temporary file. */
539*1fd5a2e1SPrashanth Swaminathan static size_t execsize = 0;
540*1fd5a2e1SPrashanth Swaminathan
541*1fd5a2e1SPrashanth Swaminathan /* Open a temporary file name, and immediately unlink it. */
542*1fd5a2e1SPrashanth Swaminathan static int
open_temp_exec_file_name(char * name,int flags)543*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_name (char *name, int flags)
544*1fd5a2e1SPrashanth Swaminathan {
545*1fd5a2e1SPrashanth Swaminathan int fd;
546*1fd5a2e1SPrashanth Swaminathan
547*1fd5a2e1SPrashanth Swaminathan #ifdef HAVE_MKOSTEMP
548*1fd5a2e1SPrashanth Swaminathan fd = mkostemp (name, flags);
549*1fd5a2e1SPrashanth Swaminathan #else
550*1fd5a2e1SPrashanth Swaminathan fd = mkstemp (name);
551*1fd5a2e1SPrashanth Swaminathan #endif
552*1fd5a2e1SPrashanth Swaminathan
553*1fd5a2e1SPrashanth Swaminathan if (fd != -1)
554*1fd5a2e1SPrashanth Swaminathan unlink (name);
555*1fd5a2e1SPrashanth Swaminathan
556*1fd5a2e1SPrashanth Swaminathan return fd;
557*1fd5a2e1SPrashanth Swaminathan }
558*1fd5a2e1SPrashanth Swaminathan
559*1fd5a2e1SPrashanth Swaminathan /* Open a temporary file in the named directory. */
560*1fd5a2e1SPrashanth Swaminathan static int
open_temp_exec_file_dir(const char * dir)561*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_dir (const char *dir)
562*1fd5a2e1SPrashanth Swaminathan {
563*1fd5a2e1SPrashanth Swaminathan static const char suffix[] = "/ffiXXXXXX";
564*1fd5a2e1SPrashanth Swaminathan int lendir, flags;
565*1fd5a2e1SPrashanth Swaminathan char *tempname;
566*1fd5a2e1SPrashanth Swaminathan #ifdef O_TMPFILE
567*1fd5a2e1SPrashanth Swaminathan int fd;
568*1fd5a2e1SPrashanth Swaminathan #endif
569*1fd5a2e1SPrashanth Swaminathan
570*1fd5a2e1SPrashanth Swaminathan #ifdef O_CLOEXEC
571*1fd5a2e1SPrashanth Swaminathan flags = O_CLOEXEC;
572*1fd5a2e1SPrashanth Swaminathan #else
573*1fd5a2e1SPrashanth Swaminathan flags = 0;
574*1fd5a2e1SPrashanth Swaminathan #endif
575*1fd5a2e1SPrashanth Swaminathan
576*1fd5a2e1SPrashanth Swaminathan #ifdef O_TMPFILE
577*1fd5a2e1SPrashanth Swaminathan fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
578*1fd5a2e1SPrashanth Swaminathan /* If the running system does not support the O_TMPFILE flag then retry without it. */
579*1fd5a2e1SPrashanth Swaminathan if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
580*1fd5a2e1SPrashanth Swaminathan return fd;
581*1fd5a2e1SPrashanth Swaminathan } else {
582*1fd5a2e1SPrashanth Swaminathan errno = 0;
583*1fd5a2e1SPrashanth Swaminathan }
584*1fd5a2e1SPrashanth Swaminathan #endif
585*1fd5a2e1SPrashanth Swaminathan
586*1fd5a2e1SPrashanth Swaminathan lendir = (int) strlen (dir);
587*1fd5a2e1SPrashanth Swaminathan tempname = __builtin_alloca (lendir + sizeof (suffix));
588*1fd5a2e1SPrashanth Swaminathan
589*1fd5a2e1SPrashanth Swaminathan if (!tempname)
590*1fd5a2e1SPrashanth Swaminathan return -1;
591*1fd5a2e1SPrashanth Swaminathan
592*1fd5a2e1SPrashanth Swaminathan memcpy (tempname, dir, lendir);
593*1fd5a2e1SPrashanth Swaminathan memcpy (tempname + lendir, suffix, sizeof (suffix));
594*1fd5a2e1SPrashanth Swaminathan
595*1fd5a2e1SPrashanth Swaminathan return open_temp_exec_file_name (tempname, flags);
596*1fd5a2e1SPrashanth Swaminathan }
597*1fd5a2e1SPrashanth Swaminathan
598*1fd5a2e1SPrashanth Swaminathan /* Open a temporary file in the directory in the named environment
599*1fd5a2e1SPrashanth Swaminathan variable. */
600*1fd5a2e1SPrashanth Swaminathan static int
open_temp_exec_file_env(const char * envvar)601*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_env (const char *envvar)
602*1fd5a2e1SPrashanth Swaminathan {
603*1fd5a2e1SPrashanth Swaminathan const char *value = getenv (envvar);
604*1fd5a2e1SPrashanth Swaminathan
605*1fd5a2e1SPrashanth Swaminathan if (!value)
606*1fd5a2e1SPrashanth Swaminathan return -1;
607*1fd5a2e1SPrashanth Swaminathan
608*1fd5a2e1SPrashanth Swaminathan return open_temp_exec_file_dir (value);
609*1fd5a2e1SPrashanth Swaminathan }
610*1fd5a2e1SPrashanth Swaminathan
611*1fd5a2e1SPrashanth Swaminathan #ifdef HAVE_MNTENT
612*1fd5a2e1SPrashanth Swaminathan /* Open a temporary file in an executable and writable mount point
613*1fd5a2e1SPrashanth Swaminathan listed in the mounts file. Subsequent calls with the same mounts
614*1fd5a2e1SPrashanth Swaminathan keep searching for mount points in the same file. Providing NULL
615*1fd5a2e1SPrashanth Swaminathan as the mounts file closes the file. */
616*1fd5a2e1SPrashanth Swaminathan static int
open_temp_exec_file_mnt(const char * mounts)617*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_mnt (const char *mounts)
618*1fd5a2e1SPrashanth Swaminathan {
619*1fd5a2e1SPrashanth Swaminathan static const char *last_mounts;
620*1fd5a2e1SPrashanth Swaminathan static FILE *last_mntent;
621*1fd5a2e1SPrashanth Swaminathan
622*1fd5a2e1SPrashanth Swaminathan if (mounts != last_mounts)
623*1fd5a2e1SPrashanth Swaminathan {
624*1fd5a2e1SPrashanth Swaminathan if (last_mntent)
625*1fd5a2e1SPrashanth Swaminathan endmntent (last_mntent);
626*1fd5a2e1SPrashanth Swaminathan
627*1fd5a2e1SPrashanth Swaminathan last_mounts = mounts;
628*1fd5a2e1SPrashanth Swaminathan
629*1fd5a2e1SPrashanth Swaminathan if (mounts)
630*1fd5a2e1SPrashanth Swaminathan last_mntent = setmntent (mounts, "r");
631*1fd5a2e1SPrashanth Swaminathan else
632*1fd5a2e1SPrashanth Swaminathan last_mntent = NULL;
633*1fd5a2e1SPrashanth Swaminathan }
634*1fd5a2e1SPrashanth Swaminathan
635*1fd5a2e1SPrashanth Swaminathan if (!last_mntent)
636*1fd5a2e1SPrashanth Swaminathan return -1;
637*1fd5a2e1SPrashanth Swaminathan
638*1fd5a2e1SPrashanth Swaminathan for (;;)
639*1fd5a2e1SPrashanth Swaminathan {
640*1fd5a2e1SPrashanth Swaminathan int fd;
641*1fd5a2e1SPrashanth Swaminathan struct mntent mnt;
642*1fd5a2e1SPrashanth Swaminathan char buf[MAXPATHLEN * 3];
643*1fd5a2e1SPrashanth Swaminathan
644*1fd5a2e1SPrashanth Swaminathan if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
645*1fd5a2e1SPrashanth Swaminathan return -1;
646*1fd5a2e1SPrashanth Swaminathan
647*1fd5a2e1SPrashanth Swaminathan if (hasmntopt (&mnt, "ro")
648*1fd5a2e1SPrashanth Swaminathan || hasmntopt (&mnt, "noexec")
649*1fd5a2e1SPrashanth Swaminathan || access (mnt.mnt_dir, W_OK))
650*1fd5a2e1SPrashanth Swaminathan continue;
651*1fd5a2e1SPrashanth Swaminathan
652*1fd5a2e1SPrashanth Swaminathan fd = open_temp_exec_file_dir (mnt.mnt_dir);
653*1fd5a2e1SPrashanth Swaminathan
654*1fd5a2e1SPrashanth Swaminathan if (fd != -1)
655*1fd5a2e1SPrashanth Swaminathan return fd;
656*1fd5a2e1SPrashanth Swaminathan }
657*1fd5a2e1SPrashanth Swaminathan }
658*1fd5a2e1SPrashanth Swaminathan #endif /* HAVE_MNTENT */
659*1fd5a2e1SPrashanth Swaminathan
660*1fd5a2e1SPrashanth Swaminathan /* Instructions to look for a location to hold a temporary file that
661*1fd5a2e1SPrashanth Swaminathan can be mapped in for execution. */
662*1fd5a2e1SPrashanth Swaminathan static struct
663*1fd5a2e1SPrashanth Swaminathan {
664*1fd5a2e1SPrashanth Swaminathan int (*func)(const char *);
665*1fd5a2e1SPrashanth Swaminathan const char *arg;
666*1fd5a2e1SPrashanth Swaminathan int repeat;
667*1fd5a2e1SPrashanth Swaminathan } open_temp_exec_file_opts[] = {
668*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_env, "TMPDIR", 0 },
669*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_dir, "/tmp", 0 },
670*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_dir, "/var/tmp", 0 },
671*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_dir, "/dev/shm", 0 },
672*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_env, "HOME", 0 },
673*1fd5a2e1SPrashanth Swaminathan #ifdef HAVE_MNTENT
674*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_mnt, "/etc/mtab", 1 },
675*1fd5a2e1SPrashanth Swaminathan { open_temp_exec_file_mnt, "/proc/mounts", 1 },
676*1fd5a2e1SPrashanth Swaminathan #endif /* HAVE_MNTENT */
677*1fd5a2e1SPrashanth Swaminathan };
678*1fd5a2e1SPrashanth Swaminathan
679*1fd5a2e1SPrashanth Swaminathan /* Current index into open_temp_exec_file_opts. */
680*1fd5a2e1SPrashanth Swaminathan static int open_temp_exec_file_opts_idx = 0;
681*1fd5a2e1SPrashanth Swaminathan
682*1fd5a2e1SPrashanth Swaminathan /* Reset a current multi-call func, then advances to the next entry.
683*1fd5a2e1SPrashanth Swaminathan If we're at the last, go back to the first and return nonzero,
684*1fd5a2e1SPrashanth Swaminathan otherwise return zero. */
685*1fd5a2e1SPrashanth Swaminathan static int
open_temp_exec_file_opts_next(void)686*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_opts_next (void)
687*1fd5a2e1SPrashanth Swaminathan {
688*1fd5a2e1SPrashanth Swaminathan if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
689*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
690*1fd5a2e1SPrashanth Swaminathan
691*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_opts_idx++;
692*1fd5a2e1SPrashanth Swaminathan if (open_temp_exec_file_opts_idx
693*1fd5a2e1SPrashanth Swaminathan == (sizeof (open_temp_exec_file_opts)
694*1fd5a2e1SPrashanth Swaminathan / sizeof (*open_temp_exec_file_opts)))
695*1fd5a2e1SPrashanth Swaminathan {
696*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_opts_idx = 0;
697*1fd5a2e1SPrashanth Swaminathan return 1;
698*1fd5a2e1SPrashanth Swaminathan }
699*1fd5a2e1SPrashanth Swaminathan
700*1fd5a2e1SPrashanth Swaminathan return 0;
701*1fd5a2e1SPrashanth Swaminathan }
702*1fd5a2e1SPrashanth Swaminathan
703*1fd5a2e1SPrashanth Swaminathan /* Return a file descriptor of a temporary zero-sized file in a
704*1fd5a2e1SPrashanth Swaminathan writable and executable filesystem. */
705*1fd5a2e1SPrashanth Swaminathan static int
open_temp_exec_file(void)706*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file (void)
707*1fd5a2e1SPrashanth Swaminathan {
708*1fd5a2e1SPrashanth Swaminathan int fd;
709*1fd5a2e1SPrashanth Swaminathan
710*1fd5a2e1SPrashanth Swaminathan do
711*1fd5a2e1SPrashanth Swaminathan {
712*1fd5a2e1SPrashanth Swaminathan fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
713*1fd5a2e1SPrashanth Swaminathan (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
714*1fd5a2e1SPrashanth Swaminathan
715*1fd5a2e1SPrashanth Swaminathan if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
716*1fd5a2e1SPrashanth Swaminathan || fd == -1)
717*1fd5a2e1SPrashanth Swaminathan {
718*1fd5a2e1SPrashanth Swaminathan if (open_temp_exec_file_opts_next ())
719*1fd5a2e1SPrashanth Swaminathan break;
720*1fd5a2e1SPrashanth Swaminathan }
721*1fd5a2e1SPrashanth Swaminathan }
722*1fd5a2e1SPrashanth Swaminathan while (fd == -1);
723*1fd5a2e1SPrashanth Swaminathan
724*1fd5a2e1SPrashanth Swaminathan return fd;
725*1fd5a2e1SPrashanth Swaminathan }
726*1fd5a2e1SPrashanth Swaminathan
727*1fd5a2e1SPrashanth Swaminathan /* We need to allocate space in a file that will be backing a writable
728*1fd5a2e1SPrashanth Swaminathan mapping. Several problems exist with the usual approaches:
729*1fd5a2e1SPrashanth Swaminathan - fallocate() is Linux-only
730*1fd5a2e1SPrashanth Swaminathan - posix_fallocate() is not available on all platforms
731*1fd5a2e1SPrashanth Swaminathan - ftruncate() does not allocate space on filesystems with sparse files
732*1fd5a2e1SPrashanth Swaminathan Failure to allocate the space will cause SIGBUS to be thrown when
733*1fd5a2e1SPrashanth Swaminathan the mapping is subsequently written to. */
734*1fd5a2e1SPrashanth Swaminathan static int
allocate_space(int fd,off_t offset,off_t len)735*1fd5a2e1SPrashanth Swaminathan allocate_space (int fd, off_t offset, off_t len)
736*1fd5a2e1SPrashanth Swaminathan {
737*1fd5a2e1SPrashanth Swaminathan static size_t page_size;
738*1fd5a2e1SPrashanth Swaminathan
739*1fd5a2e1SPrashanth Swaminathan /* Obtain system page size. */
740*1fd5a2e1SPrashanth Swaminathan if (!page_size)
741*1fd5a2e1SPrashanth Swaminathan page_size = sysconf(_SC_PAGESIZE);
742*1fd5a2e1SPrashanth Swaminathan
743*1fd5a2e1SPrashanth Swaminathan unsigned char buf[page_size];
744*1fd5a2e1SPrashanth Swaminathan memset (buf, 0, page_size);
745*1fd5a2e1SPrashanth Swaminathan
746*1fd5a2e1SPrashanth Swaminathan while (len > 0)
747*1fd5a2e1SPrashanth Swaminathan {
748*1fd5a2e1SPrashanth Swaminathan off_t to_write = (len < page_size) ? len : page_size;
749*1fd5a2e1SPrashanth Swaminathan if (write (fd, buf, to_write) < to_write)
750*1fd5a2e1SPrashanth Swaminathan return -1;
751*1fd5a2e1SPrashanth Swaminathan len -= to_write;
752*1fd5a2e1SPrashanth Swaminathan }
753*1fd5a2e1SPrashanth Swaminathan
754*1fd5a2e1SPrashanth Swaminathan return 0;
755*1fd5a2e1SPrashanth Swaminathan }
756*1fd5a2e1SPrashanth Swaminathan
757*1fd5a2e1SPrashanth Swaminathan /* Map in a chunk of memory from the temporary exec file into separate
758*1fd5a2e1SPrashanth Swaminathan locations in the virtual memory address space, one writable and one
759*1fd5a2e1SPrashanth Swaminathan executable. Returns the address of the writable portion, after
760*1fd5a2e1SPrashanth Swaminathan storing an offset to the corresponding executable portion at the
761*1fd5a2e1SPrashanth Swaminathan last word of the requested chunk. */
762*1fd5a2e1SPrashanth Swaminathan static void *
dlmmap_locked(void * start,size_t length,int prot,int flags,off_t offset)763*1fd5a2e1SPrashanth Swaminathan dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
764*1fd5a2e1SPrashanth Swaminathan {
765*1fd5a2e1SPrashanth Swaminathan void *ptr;
766*1fd5a2e1SPrashanth Swaminathan
767*1fd5a2e1SPrashanth Swaminathan if (execfd == -1)
768*1fd5a2e1SPrashanth Swaminathan {
769*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_opts_idx = 0;
770*1fd5a2e1SPrashanth Swaminathan retry_open:
771*1fd5a2e1SPrashanth Swaminathan execfd = open_temp_exec_file ();
772*1fd5a2e1SPrashanth Swaminathan if (execfd == -1)
773*1fd5a2e1SPrashanth Swaminathan return MFAIL;
774*1fd5a2e1SPrashanth Swaminathan }
775*1fd5a2e1SPrashanth Swaminathan
776*1fd5a2e1SPrashanth Swaminathan offset = execsize;
777*1fd5a2e1SPrashanth Swaminathan
778*1fd5a2e1SPrashanth Swaminathan if (allocate_space (execfd, offset, length))
779*1fd5a2e1SPrashanth Swaminathan return MFAIL;
780*1fd5a2e1SPrashanth Swaminathan
781*1fd5a2e1SPrashanth Swaminathan flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
782*1fd5a2e1SPrashanth Swaminathan flags |= MAP_SHARED;
783*1fd5a2e1SPrashanth Swaminathan
784*1fd5a2e1SPrashanth Swaminathan ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
785*1fd5a2e1SPrashanth Swaminathan flags, execfd, offset);
786*1fd5a2e1SPrashanth Swaminathan if (ptr == MFAIL)
787*1fd5a2e1SPrashanth Swaminathan {
788*1fd5a2e1SPrashanth Swaminathan if (!offset)
789*1fd5a2e1SPrashanth Swaminathan {
790*1fd5a2e1SPrashanth Swaminathan close (execfd);
791*1fd5a2e1SPrashanth Swaminathan goto retry_open;
792*1fd5a2e1SPrashanth Swaminathan }
793*1fd5a2e1SPrashanth Swaminathan if (ftruncate (execfd, offset) != 0)
794*1fd5a2e1SPrashanth Swaminathan {
795*1fd5a2e1SPrashanth Swaminathan /* Fixme : Error logs can be added here. Returning an error for
796*1fd5a2e1SPrashanth Swaminathan * ftruncte() will not add any advantage as it is being
797*1fd5a2e1SPrashanth Swaminathan * validating in the error case. */
798*1fd5a2e1SPrashanth Swaminathan }
799*1fd5a2e1SPrashanth Swaminathan
800*1fd5a2e1SPrashanth Swaminathan return MFAIL;
801*1fd5a2e1SPrashanth Swaminathan }
802*1fd5a2e1SPrashanth Swaminathan else if (!offset
803*1fd5a2e1SPrashanth Swaminathan && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
804*1fd5a2e1SPrashanth Swaminathan open_temp_exec_file_opts_next ();
805*1fd5a2e1SPrashanth Swaminathan
806*1fd5a2e1SPrashanth Swaminathan start = mmap (start, length, prot, flags, execfd, offset);
807*1fd5a2e1SPrashanth Swaminathan
808*1fd5a2e1SPrashanth Swaminathan if (start == MFAIL)
809*1fd5a2e1SPrashanth Swaminathan {
810*1fd5a2e1SPrashanth Swaminathan munmap (ptr, length);
811*1fd5a2e1SPrashanth Swaminathan if (ftruncate (execfd, offset) != 0)
812*1fd5a2e1SPrashanth Swaminathan {
813*1fd5a2e1SPrashanth Swaminathan /* Fixme : Error logs can be added here. Returning an error for
814*1fd5a2e1SPrashanth Swaminathan * ftruncte() will not add any advantage as it is being
815*1fd5a2e1SPrashanth Swaminathan * validating in the error case. */
816*1fd5a2e1SPrashanth Swaminathan }
817*1fd5a2e1SPrashanth Swaminathan return start;
818*1fd5a2e1SPrashanth Swaminathan }
819*1fd5a2e1SPrashanth Swaminathan
820*1fd5a2e1SPrashanth Swaminathan mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
821*1fd5a2e1SPrashanth Swaminathan
822*1fd5a2e1SPrashanth Swaminathan execsize += length;
823*1fd5a2e1SPrashanth Swaminathan
824*1fd5a2e1SPrashanth Swaminathan return start;
825*1fd5a2e1SPrashanth Swaminathan }
826*1fd5a2e1SPrashanth Swaminathan
827*1fd5a2e1SPrashanth Swaminathan /* Map in a writable and executable chunk of memory if possible.
828*1fd5a2e1SPrashanth Swaminathan Failing that, fall back to dlmmap_locked. */
829*1fd5a2e1SPrashanth Swaminathan static void *
dlmmap(void * start,size_t length,int prot,int flags,int fd,off_t offset)830*1fd5a2e1SPrashanth Swaminathan dlmmap (void *start, size_t length, int prot,
831*1fd5a2e1SPrashanth Swaminathan int flags, int fd, off_t offset)
832*1fd5a2e1SPrashanth Swaminathan {
833*1fd5a2e1SPrashanth Swaminathan void *ptr;
834*1fd5a2e1SPrashanth Swaminathan
835*1fd5a2e1SPrashanth Swaminathan assert (start == NULL && length % malloc_getpagesize == 0
836*1fd5a2e1SPrashanth Swaminathan && prot == (PROT_READ | PROT_WRITE)
837*1fd5a2e1SPrashanth Swaminathan && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
838*1fd5a2e1SPrashanth Swaminathan && fd == -1 && offset == 0);
839*1fd5a2e1SPrashanth Swaminathan
840*1fd5a2e1SPrashanth Swaminathan if (execfd == -1 && is_emutramp_enabled ())
841*1fd5a2e1SPrashanth Swaminathan {
842*1fd5a2e1SPrashanth Swaminathan ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
843*1fd5a2e1SPrashanth Swaminathan return ptr;
844*1fd5a2e1SPrashanth Swaminathan }
845*1fd5a2e1SPrashanth Swaminathan
846*1fd5a2e1SPrashanth Swaminathan if (execfd == -1 && !is_selinux_enabled ())
847*1fd5a2e1SPrashanth Swaminathan {
848*1fd5a2e1SPrashanth Swaminathan ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
849*1fd5a2e1SPrashanth Swaminathan
850*1fd5a2e1SPrashanth Swaminathan if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
851*1fd5a2e1SPrashanth Swaminathan /* Cool, no need to mess with separate segments. */
852*1fd5a2e1SPrashanth Swaminathan return ptr;
853*1fd5a2e1SPrashanth Swaminathan
854*1fd5a2e1SPrashanth Swaminathan /* If MREMAP_DUP is ever introduced and implemented, try mmap
855*1fd5a2e1SPrashanth Swaminathan with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
856*1fd5a2e1SPrashanth Swaminathan MREMAP_DUP and prot at this point. */
857*1fd5a2e1SPrashanth Swaminathan }
858*1fd5a2e1SPrashanth Swaminathan
859*1fd5a2e1SPrashanth Swaminathan if (execsize == 0 || execfd == -1)
860*1fd5a2e1SPrashanth Swaminathan {
861*1fd5a2e1SPrashanth Swaminathan pthread_mutex_lock (&open_temp_exec_file_mutex);
862*1fd5a2e1SPrashanth Swaminathan ptr = dlmmap_locked (start, length, prot, flags, offset);
863*1fd5a2e1SPrashanth Swaminathan pthread_mutex_unlock (&open_temp_exec_file_mutex);
864*1fd5a2e1SPrashanth Swaminathan
865*1fd5a2e1SPrashanth Swaminathan return ptr;
866*1fd5a2e1SPrashanth Swaminathan }
867*1fd5a2e1SPrashanth Swaminathan
868*1fd5a2e1SPrashanth Swaminathan return dlmmap_locked (start, length, prot, flags, offset);
869*1fd5a2e1SPrashanth Swaminathan }
870*1fd5a2e1SPrashanth Swaminathan
871*1fd5a2e1SPrashanth Swaminathan /* Release memory at the given address, as well as the corresponding
872*1fd5a2e1SPrashanth Swaminathan executable page if it's separate. */
873*1fd5a2e1SPrashanth Swaminathan static int
dlmunmap(void * start,size_t length)874*1fd5a2e1SPrashanth Swaminathan dlmunmap (void *start, size_t length)
875*1fd5a2e1SPrashanth Swaminathan {
876*1fd5a2e1SPrashanth Swaminathan /* We don't bother decreasing execsize or truncating the file, since
877*1fd5a2e1SPrashanth Swaminathan we can't quite tell whether we're unmapping the end of the file.
878*1fd5a2e1SPrashanth Swaminathan We don't expect frequent deallocation anyway. If we did, we
879*1fd5a2e1SPrashanth Swaminathan could locate pages in the file by writing to the pages being
880*1fd5a2e1SPrashanth Swaminathan deallocated and checking that the file contents change.
881*1fd5a2e1SPrashanth Swaminathan Yuck. */
882*1fd5a2e1SPrashanth Swaminathan msegmentptr seg = segment_holding (gm, start);
883*1fd5a2e1SPrashanth Swaminathan void *code;
884*1fd5a2e1SPrashanth Swaminathan
885*1fd5a2e1SPrashanth Swaminathan if (seg && (code = add_segment_exec_offset (start, seg)) != start)
886*1fd5a2e1SPrashanth Swaminathan {
887*1fd5a2e1SPrashanth Swaminathan int ret = munmap (code, length);
888*1fd5a2e1SPrashanth Swaminathan if (ret)
889*1fd5a2e1SPrashanth Swaminathan return ret;
890*1fd5a2e1SPrashanth Swaminathan }
891*1fd5a2e1SPrashanth Swaminathan
892*1fd5a2e1SPrashanth Swaminathan return munmap (start, length);
893*1fd5a2e1SPrashanth Swaminathan }
894*1fd5a2e1SPrashanth Swaminathan
895*1fd5a2e1SPrashanth Swaminathan #if FFI_CLOSURE_FREE_CODE
896*1fd5a2e1SPrashanth Swaminathan /* Return segment holding given code address. */
897*1fd5a2e1SPrashanth Swaminathan static msegmentptr
segment_holding_code(mstate m,char * addr)898*1fd5a2e1SPrashanth Swaminathan segment_holding_code (mstate m, char* addr)
899*1fd5a2e1SPrashanth Swaminathan {
900*1fd5a2e1SPrashanth Swaminathan msegmentptr sp = &m->seg;
901*1fd5a2e1SPrashanth Swaminathan for (;;) {
902*1fd5a2e1SPrashanth Swaminathan if (addr >= add_segment_exec_offset (sp->base, sp)
903*1fd5a2e1SPrashanth Swaminathan && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
904*1fd5a2e1SPrashanth Swaminathan return sp;
905*1fd5a2e1SPrashanth Swaminathan if ((sp = sp->next) == 0)
906*1fd5a2e1SPrashanth Swaminathan return 0;
907*1fd5a2e1SPrashanth Swaminathan }
908*1fd5a2e1SPrashanth Swaminathan }
909*1fd5a2e1SPrashanth Swaminathan #endif
910*1fd5a2e1SPrashanth Swaminathan
911*1fd5a2e1SPrashanth Swaminathan #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
912*1fd5a2e1SPrashanth Swaminathan
913*1fd5a2e1SPrashanth Swaminathan /* Allocate a chunk of memory with the given size. Returns a pointer
914*1fd5a2e1SPrashanth Swaminathan to the writable address, and sets *CODE to the executable
915*1fd5a2e1SPrashanth Swaminathan corresponding virtual address. */
916*1fd5a2e1SPrashanth Swaminathan void *
ffi_closure_alloc(size_t size,void ** code)917*1fd5a2e1SPrashanth Swaminathan ffi_closure_alloc (size_t size, void **code)
918*1fd5a2e1SPrashanth Swaminathan {
919*1fd5a2e1SPrashanth Swaminathan void *ptr;
920*1fd5a2e1SPrashanth Swaminathan
921*1fd5a2e1SPrashanth Swaminathan if (!code)
922*1fd5a2e1SPrashanth Swaminathan return NULL;
923*1fd5a2e1SPrashanth Swaminathan
924*1fd5a2e1SPrashanth Swaminathan ptr = dlmalloc (size);
925*1fd5a2e1SPrashanth Swaminathan
926*1fd5a2e1SPrashanth Swaminathan if (ptr)
927*1fd5a2e1SPrashanth Swaminathan {
928*1fd5a2e1SPrashanth Swaminathan msegmentptr seg = segment_holding (gm, ptr);
929*1fd5a2e1SPrashanth Swaminathan
930*1fd5a2e1SPrashanth Swaminathan *code = add_segment_exec_offset (ptr, seg);
931*1fd5a2e1SPrashanth Swaminathan }
932*1fd5a2e1SPrashanth Swaminathan
933*1fd5a2e1SPrashanth Swaminathan return ptr;
934*1fd5a2e1SPrashanth Swaminathan }
935*1fd5a2e1SPrashanth Swaminathan
936*1fd5a2e1SPrashanth Swaminathan void *
ffi_data_to_code_pointer(void * data)937*1fd5a2e1SPrashanth Swaminathan ffi_data_to_code_pointer (void *data)
938*1fd5a2e1SPrashanth Swaminathan {
939*1fd5a2e1SPrashanth Swaminathan msegmentptr seg = segment_holding (gm, data);
940*1fd5a2e1SPrashanth Swaminathan /* We expect closures to be allocated with ffi_closure_alloc(), in
941*1fd5a2e1SPrashanth Swaminathan which case seg will be non-NULL. However, some users take on the
942*1fd5a2e1SPrashanth Swaminathan burden of managing this memory themselves, in which case this
943*1fd5a2e1SPrashanth Swaminathan we'll just return data. */
944*1fd5a2e1SPrashanth Swaminathan if (seg)
945*1fd5a2e1SPrashanth Swaminathan return add_segment_exec_offset (data, seg);
946*1fd5a2e1SPrashanth Swaminathan else
947*1fd5a2e1SPrashanth Swaminathan return data;
948*1fd5a2e1SPrashanth Swaminathan }
949*1fd5a2e1SPrashanth Swaminathan
950*1fd5a2e1SPrashanth Swaminathan /* Release a chunk of memory allocated with ffi_closure_alloc. If
951*1fd5a2e1SPrashanth Swaminathan FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
952*1fd5a2e1SPrashanth Swaminathan writable or the executable address given. Otherwise, only the
953*1fd5a2e1SPrashanth Swaminathan writable address can be provided here. */
954*1fd5a2e1SPrashanth Swaminathan void
ffi_closure_free(void * ptr)955*1fd5a2e1SPrashanth Swaminathan ffi_closure_free (void *ptr)
956*1fd5a2e1SPrashanth Swaminathan {
957*1fd5a2e1SPrashanth Swaminathan #if FFI_CLOSURE_FREE_CODE
958*1fd5a2e1SPrashanth Swaminathan msegmentptr seg = segment_holding_code (gm, ptr);
959*1fd5a2e1SPrashanth Swaminathan
960*1fd5a2e1SPrashanth Swaminathan if (seg)
961*1fd5a2e1SPrashanth Swaminathan ptr = sub_segment_exec_offset (ptr, seg);
962*1fd5a2e1SPrashanth Swaminathan #endif
963*1fd5a2e1SPrashanth Swaminathan
964*1fd5a2e1SPrashanth Swaminathan dlfree (ptr);
965*1fd5a2e1SPrashanth Swaminathan }
966*1fd5a2e1SPrashanth Swaminathan
967*1fd5a2e1SPrashanth Swaminathan # else /* ! FFI_MMAP_EXEC_WRIT */
968*1fd5a2e1SPrashanth Swaminathan
969*1fd5a2e1SPrashanth Swaminathan /* On many systems, memory returned by malloc is writable and
970*1fd5a2e1SPrashanth Swaminathan executable, so just use it. */
971*1fd5a2e1SPrashanth Swaminathan
972*1fd5a2e1SPrashanth Swaminathan #include <stdlib.h>
973*1fd5a2e1SPrashanth Swaminathan
974*1fd5a2e1SPrashanth Swaminathan void *
ffi_closure_alloc(size_t size,void ** code)975*1fd5a2e1SPrashanth Swaminathan ffi_closure_alloc (size_t size, void **code)
976*1fd5a2e1SPrashanth Swaminathan {
977*1fd5a2e1SPrashanth Swaminathan if (!code)
978*1fd5a2e1SPrashanth Swaminathan return NULL;
979*1fd5a2e1SPrashanth Swaminathan
980*1fd5a2e1SPrashanth Swaminathan return *code = malloc (size);
981*1fd5a2e1SPrashanth Swaminathan }
982*1fd5a2e1SPrashanth Swaminathan
983*1fd5a2e1SPrashanth Swaminathan void
ffi_closure_free(void * ptr)984*1fd5a2e1SPrashanth Swaminathan ffi_closure_free (void *ptr)
985*1fd5a2e1SPrashanth Swaminathan {
986*1fd5a2e1SPrashanth Swaminathan free (ptr);
987*1fd5a2e1SPrashanth Swaminathan }
988*1fd5a2e1SPrashanth Swaminathan
989*1fd5a2e1SPrashanth Swaminathan void *
ffi_data_to_code_pointer(void * data)990*1fd5a2e1SPrashanth Swaminathan ffi_data_to_code_pointer (void *data)
991*1fd5a2e1SPrashanth Swaminathan {
992*1fd5a2e1SPrashanth Swaminathan return data;
993*1fd5a2e1SPrashanth Swaminathan }
994*1fd5a2e1SPrashanth Swaminathan
995*1fd5a2e1SPrashanth Swaminathan # endif /* ! FFI_MMAP_EXEC_WRIT */
996*1fd5a2e1SPrashanth Swaminathan #endif /* FFI_CLOSURES */
997*1fd5a2e1SPrashanth Swaminathan
998*1fd5a2e1SPrashanth Swaminathan #endif /* NetBSD with PROT_MPROTECT */
999