xref: /aosp_15_r20/external/libffi/src/closures.c (revision 1fd5a2e1d639cd1ddf29dd0c484c123bbd850c21)
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