ada: Disable PIE mode during the build of the Ada front-end
[official-gcc.git] / libffi / src / tramp.c
blob265aeaa3cdf8d8a77c27c7d00a33b84e8f227cf2
1 /* -----------------------------------------------------------------------
2 tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman
4 API and support functions for managing statically defined closure
5 trampolines.
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 ``Software''), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 ----------------------------------------------------------------------- */
28 #include <fficonfig.h>
30 #ifdef FFI_EXEC_STATIC_TRAMP
32 /* -------------------------- Headers and Definitions ---------------------*/
34 * Add support for other OSes later. For now, it is just Linux.
37 #if defined __linux__
38 #ifdef __linux__
39 #define _GNU_SOURCE 1
40 #endif
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <stdint.h>
45 #include <fcntl.h>
46 #include <pthread.h>
47 #include <sys/mman.h>
48 #include <tramp.h>
49 #ifdef __linux__
50 #include <linux/limits.h>
51 #include <linux/types.h>
52 #endif
53 #endif /* __linux__ */
56 * Each architecture defines static code for a trampoline code table. The
57 * trampoline code table is mapped into the address space of a process.
59 * The following architecture specific function returns:
61 * - the address of the trampoline code table in the text segment
62 * - the size of each trampoline in the trampoline code table
63 * - the size of the mapping for the whole trampoline code table
65 void __attribute__((weak)) *ffi_tramp_arch (size_t *tramp_size,
66 size_t *map_size);
68 /* ------------------------- Trampoline Data Structures --------------------*/
70 struct tramp;
73 * Trampoline table. Manages one trampoline code table and one trampoline
74 * parameter table.
76 * prev, next Links in the global trampoline table list.
77 * code_table Trampoline code table mapping.
78 * parm_table Trampoline parameter table mapping.
79 * array Array of trampolines malloced.
80 * free List of free trampolines.
81 * nfree Number of free trampolines.
83 struct tramp_table
85 struct tramp_table *prev;
86 struct tramp_table *next;
87 void *code_table;
88 void *parm_table;
89 struct tramp *array;
90 struct tramp *free;
91 int nfree;
95 * Parameters for each trampoline.
97 * data
98 * Data for the target code that the trampoline jumps to.
99 * target
100 * Target code that the trampoline jumps to.
102 struct tramp_parm
104 void *data;
105 void *target;
109 * Trampoline structure for each trampoline.
111 * prev, next Links in the trampoline free list of a trampoline table.
112 * table Trampoline table to which this trampoline belongs.
113 * code Address of this trampoline in the code table mapping.
114 * parm Address of this trampoline's parameters in the parameter
115 * table mapping.
117 struct tramp
119 struct tramp *prev;
120 struct tramp *next;
121 struct tramp_table *table;
122 void *code;
123 struct tramp_parm *parm;
126 enum tramp_globals_status {
127 TRAMP_GLOBALS_UNINITIALIZED = 0,
128 TRAMP_GLOBALS_PASSED,
129 TRAMP_GLOBALS_FAILED,
133 * Trampoline globals.
135 * fd
136 * File descriptor of binary file that contains the trampoline code table.
137 * offset
138 * Offset of the trampoline code table in that file.
139 * text
140 * Address of the trampoline code table in the text segment.
141 * map_size
142 * Size of the trampoline code table mapping.
143 * size
144 * Size of one trampoline in the trampoline code table.
145 * ntramp
146 * Total number of trampolines in the trampoline code table.
147 * free_tables
148 * List of trampoline tables that contain free trampolines.
149 * nfree_tables
150 * Number of trampoline tables that contain free trampolines.
151 * status
152 * Initialization status.
154 struct tramp_globals
156 int fd;
157 off_t offset;
158 void *text;
159 size_t map_size;
160 size_t size;
161 int ntramp;
162 struct tramp_table *free_tables;
163 int nfree_tables;
164 enum tramp_globals_status status;
167 static struct tramp_globals tramp_globals;
169 /* --------------------- Trampoline File Initialization --------------------*/
172 * The trampoline file is the file used to map the trampoline code table into
173 * the address space of a process. There are two ways to get this file:
175 * - From the OS. E.g., on Linux, /proc/<pid>/maps lists all the memory
176 * mappings for <pid>. For file-backed mappings, maps supplies the file name
177 * and the file offset. Using this, we can locate the mapping that maps
178 * libffi and get the path to the libffi binary. And, we can compute the
179 * offset of the trampoline code table within that binary.
181 * - Else, if we can create a temporary file, we can write the trampoline code
182 * table from the text segment into the temporary file.
184 * The first method is the preferred one. If the OS security subsystem
185 * disallows mapping unsigned files with PROT_EXEC, then the second method
186 * will fail.
188 * If an OS allows the trampoline code table in the text segment to be
189 * directly remapped (e.g., MACH vm_remap ()), then we don't need the
190 * trampoline file.
192 static int tramp_table_alloc (void);
194 #if defined __linux__
196 static int
197 ffi_tramp_get_libffi (void)
199 FILE *fp;
200 char file[PATH_MAX], line[PATH_MAX+100], perm[10], dev[10];
201 unsigned long start, end, offset, inode;
202 uintptr_t addr = (uintptr_t) tramp_globals.text;
203 int nfields, found;
205 snprintf (file, PATH_MAX, "/proc/%d/maps", getpid());
206 fp = fopen (file, "r");
207 if (fp == NULL)
208 return 0;
210 found = 0;
211 while (feof (fp) == 0) {
212 if (fgets (line, sizeof (line), fp) == 0)
213 break;
215 nfields = sscanf (line, "%lx-%lx %9s %lx %9s %ld %s",
216 &start, &end, perm, &offset, dev, &inode, file);
217 if (nfields != 7)
218 continue;
220 if (addr >= start && addr < end) {
221 tramp_globals.offset = offset + (addr - start);
222 found = 1;
223 break;
226 fclose (fp);
228 if (!found)
229 return 0;
231 tramp_globals.fd = open (file, O_RDONLY);
232 if (tramp_globals.fd == -1)
233 return 0;
236 * Allocate a trampoline table just to make sure that the trampoline code
237 * table can be mapped.
239 if (!tramp_table_alloc ())
241 close (tramp_globals.fd);
242 tramp_globals.fd = -1;
243 return 0;
245 return 1;
248 #endif /* __linux__ */
250 #if defined __linux__
252 #if defined HAVE_MKSTEMP
254 static int
255 ffi_tramp_get_temp_file (void)
257 char template[12] = "/tmp/XXXXXX";
258 ssize_t count;
260 tramp_globals.offset = 0;
261 tramp_globals.fd = mkstemp (template);
262 if (tramp_globals.fd == -1)
263 return 0;
265 unlink (template);
267 * Write the trampoline code table into the temporary file and allocate a
268 * trampoline table to make sure that the temporary file can be mapped.
270 count = write(tramp_globals.fd, tramp_globals.text, tramp_globals.map_size);
271 if (count == tramp_globals.map_size && tramp_table_alloc ())
272 return 1;
274 close (tramp_globals.fd);
275 tramp_globals.fd = -1;
276 return 0;
279 #else /* !defined HAVE_MKSTEMP */
282 * TODO:
283 * src/closures.c contains code for finding temp file that has EXEC
284 * permissions. May be, some of that code can be shared with static
285 * trampolines.
287 static int
288 ffi_tramp_get_temp_file (void)
290 tramp_globals.offset = 0;
291 tramp_globals.fd = -1;
292 return 0;
295 #endif /* defined HAVE_MKSTEMP */
297 #endif /* __linux__ */
299 /* ------------------------ OS-specific Initialization ----------------------*/
301 #if defined __linux__
303 static int
304 ffi_tramp_init_os (void)
306 if (ffi_tramp_get_libffi ())
307 return 1;
308 return ffi_tramp_get_temp_file ();
311 #endif /* __linux__ */
313 /* --------------------------- OS-specific Locking -------------------------*/
315 #if defined __linux__
317 static pthread_mutex_t tramp_globals_mutex = PTHREAD_MUTEX_INITIALIZER;
319 static void
320 ffi_tramp_lock(void)
322 pthread_mutex_lock (&tramp_globals_mutex);
325 static void
326 ffi_tramp_unlock()
328 pthread_mutex_unlock (&tramp_globals_mutex);
331 #endif /* __linux__ */
333 /* ------------------------ OS-specific Memory Mapping ----------------------*/
336 * Create a trampoline code table mapping and a trampoline parameter table
337 * mapping. The two mappings must be adjacent to each other for PC-relative
338 * access.
340 * For each trampoline in the code table, there is a corresponding parameter
341 * block in the parameter table. The size of the parameter block is the same
342 * as the size of the trampoline. This means that the parameter block is at
343 * a fixed offset from its trampoline making it easy for a trampoline to find
344 * its parameters using PC-relative access.
346 * The parameter block will contain a struct tramp_parm. This means that
347 * sizeof (struct tramp_parm) cannot exceed the size of a parameter block.
350 #if defined __linux__
352 static int
353 tramp_table_map (struct tramp_table *table)
355 char *addr;
358 * Create an anonymous mapping twice the map size. The top half will be used
359 * for the code table. The bottom half will be used for the parameter table.
361 addr = mmap (NULL, tramp_globals.map_size * 2, PROT_READ | PROT_WRITE,
362 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
363 if (addr == MAP_FAILED)
364 return 0;
367 * Replace the top half of the anonymous mapping with the code table mapping.
369 table->code_table = mmap (addr, tramp_globals.map_size, PROT_READ | PROT_EXEC,
370 MAP_PRIVATE | MAP_FIXED, tramp_globals.fd, tramp_globals.offset);
371 if (table->code_table == MAP_FAILED)
373 (void) munmap (addr, tramp_globals.map_size * 2);
374 return 0;
376 table->parm_table = table->code_table + tramp_globals.map_size;
377 return 1;
380 static void
381 tramp_table_unmap (struct tramp_table *table)
383 (void) munmap (table->code_table, tramp_globals.map_size);
384 (void) munmap (table->parm_table, tramp_globals.map_size);
387 #endif /* __linux__ */
389 /* ------------------------ Trampoline Initialization ----------------------*/
392 * Initialize the static trampoline feature.
394 static int
395 ffi_tramp_init (void)
397 if (tramp_globals.status == TRAMP_GLOBALS_PASSED)
398 return 1;
400 if (tramp_globals.status == TRAMP_GLOBALS_FAILED)
401 return 0;
403 if (ffi_tramp_arch == NULL)
405 tramp_globals.status = TRAMP_GLOBALS_FAILED;
406 return 0;
409 tramp_globals.free_tables = NULL;
410 tramp_globals.nfree_tables = 0;
413 * Get trampoline code table information from the architecture.
415 tramp_globals.text = ffi_tramp_arch (&tramp_globals.size,
416 &tramp_globals.map_size);
417 tramp_globals.ntramp = tramp_globals.map_size / tramp_globals.size;
419 if (sysconf (_SC_PAGESIZE) > tramp_globals.map_size)
420 return 0;
422 if (ffi_tramp_init_os ())
424 tramp_globals.status = TRAMP_GLOBALS_PASSED;
425 return 1;
428 tramp_globals.status = TRAMP_GLOBALS_FAILED;
429 return 0;
432 /* ---------------------- Trampoline Table functions ---------------------- */
434 /* This code assumes that malloc () is available on all OSes. */
436 static void tramp_add (struct tramp *tramp);
439 * Allocate and initialize a trampoline table.
441 static int
442 tramp_table_alloc (void)
444 struct tramp_table *table;
445 struct tramp *tramp_array, *tramp;
446 size_t size;
447 char *code, *parm;
448 int i;
451 * If we already have tables with free trampolines, there is no need to
452 * allocate a new table.
454 if (tramp_globals.nfree_tables > 0)
455 return 1;
458 * Allocate a new trampoline table structure.
460 table = malloc (sizeof (*table));
461 if (table == NULL)
462 return 0;
465 * Allocate new trampoline structures.
467 tramp_array = malloc (sizeof (*tramp) * tramp_globals.ntramp);
468 if (tramp_array == NULL)
469 goto free_table;
472 * Map a code table and a parameter table into the caller's address space.
474 if (!tramp_table_map (table))
477 * Failed to map the code and parameter tables.
479 goto free_tramp_array;
483 * Initialize the trampoline table.
485 table->array = tramp_array;
486 table->free = NULL;
487 table->nfree = 0;
490 * Populate the trampoline table free list. This will also add the trampoline
491 * table to the global list of trampoline tables.
493 size = tramp_globals.size;
494 code = table->code_table;
495 parm = table->parm_table;
496 for (i = 0; i < tramp_globals.ntramp; i++)
498 tramp = &tramp_array[i];
499 tramp->table = table;
500 tramp->code = code;
501 tramp->parm = (struct tramp_parm *) parm;
502 tramp_add (tramp);
504 code += size;
505 parm += size;
507 /* Success */
508 return 1;
510 /* Failure */
511 free_tramp_array:
512 free (tramp_array);
513 free_table:
514 free (table);
515 return 0;
519 * Free a trampoline table.
521 static void
522 tramp_table_free (struct tramp_table *table)
524 tramp_table_unmap (table);
525 free (table->array);
526 free (table);
530 * Add a new trampoline table to the global table list.
532 static void
533 tramp_table_add (struct tramp_table *table)
535 table->next = tramp_globals.free_tables;
536 table->prev = NULL;
537 if (tramp_globals.free_tables != NULL)
538 tramp_globals.free_tables->prev = table;
539 tramp_globals.free_tables = table;
540 tramp_globals.nfree_tables++;
544 * Delete a trampoline table from the global table list.
546 static void
547 tramp_table_del (struct tramp_table *table)
549 tramp_globals.nfree_tables--;
550 if (table->prev != NULL)
551 table->prev->next = table->next;
552 if (table->next != NULL)
553 table->next->prev = table->prev;
554 if (tramp_globals.free_tables == table)
555 tramp_globals.free_tables = table->next;
558 /* ------------------------- Trampoline functions ------------------------- */
561 * Add a trampoline to its trampoline table.
563 static void
564 tramp_add (struct tramp *tramp)
566 struct tramp_table *table = tramp->table;
568 tramp->next = table->free;
569 tramp->prev = NULL;
570 if (table->free != NULL)
571 table->free->prev = tramp;
572 table->free = tramp;
573 table->nfree++;
575 if (table->nfree == 1)
576 tramp_table_add (table);
579 * We don't want to keep too many free trampoline tables lying around.
581 if (table->nfree == tramp_globals.ntramp &&
582 tramp_globals.nfree_tables > 1)
584 tramp_table_del (table);
585 tramp_table_free (table);
590 * Remove a trampoline from its trampoline table.
592 static void
593 tramp_del (struct tramp *tramp)
595 struct tramp_table *table = tramp->table;
597 table->nfree--;
598 if (tramp->prev != NULL)
599 tramp->prev->next = tramp->next;
600 if (tramp->next != NULL)
601 tramp->next->prev = tramp->prev;
602 if (table->free == tramp)
603 table->free = tramp->next;
605 if (table->nfree == 0)
606 tramp_table_del (table);
609 /* ------------------------ Trampoline API functions ------------------------ */
612 ffi_tramp_is_supported(void)
614 int ret;
616 ffi_tramp_lock();
617 ret = ffi_tramp_init ();
618 ffi_tramp_unlock();
619 return ret;
623 * Allocate a trampoline and return its opaque address.
625 void *
626 ffi_tramp_alloc (int flags)
628 struct tramp *tramp;
630 ffi_tramp_lock();
632 if (!ffi_tramp_init () || flags != 0)
634 ffi_tramp_unlock();
635 return NULL;
638 if (!tramp_table_alloc ())
640 ffi_tramp_unlock();
641 return NULL;
644 tramp = tramp_globals.free_tables->free;
645 tramp_del (tramp);
647 ffi_tramp_unlock();
649 return tramp;
653 * Set the parameters for a trampoline.
655 void
656 ffi_tramp_set_parms (void *arg, void *target, void *data)
658 struct tramp *tramp = arg;
660 ffi_tramp_lock();
661 tramp->parm->target = target;
662 tramp->parm->data = data;
663 ffi_tramp_unlock();
667 * Get the invocation address of a trampoline.
669 void *
670 ffi_tramp_get_addr (void *arg)
672 struct tramp *tramp = arg;
673 void *addr;
675 ffi_tramp_lock();
676 addr = tramp->code;
677 ffi_tramp_unlock();
679 return addr;
683 * Free a trampoline.
685 void
686 ffi_tramp_free (void *arg)
688 struct tramp *tramp = arg;
690 ffi_tramp_lock();
691 tramp_add (tramp);
692 ffi_tramp_unlock();
695 /* ------------------------------------------------------------------------- */
697 #else /* !FFI_EXEC_STATIC_TRAMP */
699 #include <stddef.h>
702 ffi_tramp_is_supported(void)
704 return 0;
707 void *
708 ffi_tramp_alloc (int flags)
710 return NULL;
713 void
714 ffi_tramp_set_parms (void *arg, void *target, void *data)
718 void *
719 ffi_tramp_get_addr (void *arg)
721 return NULL;
724 void
725 ffi_tramp_free (void *arg)
729 #endif /* FFI_EXEC_STATIC_TRAMP */