Static tramp v5 (#624)
[platform/upstream/libffi.git] / src / tramp.c
1 /* -----------------------------------------------------------------------
2    tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman
3
4    API and support functions for managing statically defined closure
5    trampolines.
6
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:
14
15    The above copyright notice and this permission notice shall be included
16    in all copies or substantial portions of the Software.
17
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    ----------------------------------------------------------------------- */
27
28 #include <fficonfig.h>
29
30 #ifdef FFI_EXEC_STATIC_TRAMP
31
32 /* -------------------------- Headers and Definitions ---------------------*/
33 /*
34  * Add support for other OSes later. For now, it is just Linux.
35  */
36
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__ */
54
55 /*
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.
58  *
59  * The following architecture specific function returns:
60  *
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
64  */
65 void __attribute__((weak)) *ffi_tramp_arch (size_t *tramp_size,
66   size_t *map_size);
67
68 /* ------------------------- Trampoline Data Structures --------------------*/
69
70 struct tramp;
71
72 /*
73  * Trampoline table. Manages one trampoline code table and one trampoline
74  * parameter table.
75  *
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.
82  */
83 struct tramp_table
84 {
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;
92 };
93
94 /*
95  * Parameters for each trampoline.
96  *
97  * data
98  *      Data for the target code that the trampoline jumps to.
99  * target
100  *      Target code that the trampoline jumps to.
101  */
102 struct tramp_parm
103 {
104   void *data;
105   void *target;
106 };
107
108 /*
109  * Trampoline structure for each trampoline.
110  *
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.
116  */
117 struct tramp
118 {
119   struct tramp *prev;
120   struct tramp *next;
121   struct tramp_table *table;
122   void *code;
123   struct tramp_parm *parm;
124 };
125
126 enum tramp_globals_status {
127         TRAMP_GLOBALS_UNINITIALIZED = 0,
128         TRAMP_GLOBALS_PASSED,
129         TRAMP_GLOBALS_FAILED,
130 };
131
132 /*
133  * Trampoline globals.
134  *
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.
153  */
154 struct tramp_globals
155 {
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;
165 };
166
167 static struct tramp_globals tramp_globals;
168
169 /* --------------------- Trampoline File Initialization --------------------*/
170
171 /*
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:
174  *
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.
180  *
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.
183  *
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.
187  *
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.
191  */
192 static int tramp_table_alloc (void);
193
194 #if defined __linux__
195
196 static int
197 ffi_tramp_get_libffi (void)
198 {
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;
204
205   snprintf (file, PATH_MAX, "/proc/%d/maps", getpid());
206   fp = fopen (file, "r");
207   if (fp == NULL)
208     return 0;
209
210   found = 0;
211   while (feof (fp) == 0) {
212     if (fgets (line, sizeof (line), fp) == 0)
213       break;
214
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;
219
220     if (addr >= start && addr < end) {
221       tramp_globals.offset = offset + (addr - start);
222       found = 1;
223       break;
224     }
225   }
226   fclose (fp);
227
228   if (!found)
229     return 0;
230
231   tramp_globals.fd = open (file, O_RDONLY);
232   if (tramp_globals.fd == -1)
233     return 0;
234
235   /*
236    * Allocate a trampoline table just to make sure that the trampoline code
237    * table can be mapped.
238    */
239   if (!tramp_table_alloc ())
240     {
241       close (tramp_globals.fd);
242       tramp_globals.fd = -1;
243       return 0;
244     }
245   return 1;
246 }
247
248 #endif /* __linux__ */
249
250 #if defined __linux__
251
252 #if defined HAVE_MKSTEMP
253
254 static int
255 ffi_tramp_get_temp_file (void)
256 {
257   char template[12] = "/tmp/XXXXXX";
258   ssize_t count;
259
260   tramp_globals.offset = 0;
261   tramp_globals.fd = mkstemp (template);
262   if (tramp_globals.fd == -1)
263     return 0;
264
265   unlink (template);
266   /*
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.
269    */
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;
273
274   close (tramp_globals.fd);
275   tramp_globals.fd = -1;
276   return 0;
277 }
278
279 #else /* !defined HAVE_MKSTEMP */
280
281 /*
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.
286  */
287 static int
288 ffi_tramp_get_temp_file (void)
289 {
290   tramp_globals.offset = 0;
291   tramp_globals.fd = -1;
292   return 0;
293 }
294
295 #endif /* defined HAVE_MKSTEMP */
296
297 #endif /* __linux__ */
298
299 /* ------------------------ OS-specific Initialization ----------------------*/
300
301 #if defined __linux__
302
303 static int
304 ffi_tramp_init_os (void)
305 {
306   if (ffi_tramp_get_libffi ())
307     return 1;
308   return ffi_tramp_get_temp_file ();
309 }
310
311 #endif /* __linux__ */
312
313 /* --------------------------- OS-specific Locking -------------------------*/
314
315 #if defined __linux__
316
317 static pthread_mutex_t tramp_globals_mutex = PTHREAD_MUTEX_INITIALIZER;
318
319 static void
320 ffi_tramp_lock(void)
321 {
322   pthread_mutex_lock (&tramp_globals_mutex);
323 }
324
325 static void
326 ffi_tramp_unlock()
327 {
328   pthread_mutex_unlock (&tramp_globals_mutex);
329 }
330
331 #endif /* __linux__ */
332
333 /* ------------------------ OS-specific Memory Mapping ----------------------*/
334
335 /*
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.
339  *
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.
345  *
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.
348  */
349
350 #if defined __linux__
351
352 static int
353 tramp_table_map (struct tramp_table *table)
354 {
355   char *addr;
356
357   /*
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.
360    */
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;
365
366   /*
367    * Replace the top half of the anonymous mapping with the code table mapping.
368    */
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)
372     {
373       (void) munmap (addr, tramp_globals.map_size * 2);
374       return 0;
375     }
376   table->parm_table = table->code_table + tramp_globals.map_size;
377   return 1;
378 }
379
380 static void
381 tramp_table_unmap (struct tramp_table *table)
382 {
383   (void) munmap (table->code_table, tramp_globals.map_size);
384   (void) munmap (table->parm_table, tramp_globals.map_size);
385 }
386
387 #endif /* __linux__ */
388
389 /* ------------------------ Trampoline Initialization ----------------------*/
390
391 /*
392  * Initialize the static trampoline feature.
393  */
394 static int
395 ffi_tramp_init (void)
396 {
397   if (tramp_globals.status == TRAMP_GLOBALS_PASSED)
398     return 1;
399
400   if (tramp_globals.status == TRAMP_GLOBALS_FAILED)
401     return 0;
402
403   if (ffi_tramp_arch == NULL)
404     {
405       tramp_globals.status = TRAMP_GLOBALS_FAILED;
406       return 0;
407     }
408
409   tramp_globals.free_tables = NULL;
410   tramp_globals.nfree_tables = 0;
411
412   /*
413    * Get trampoline code table information from the architecture.
414    */
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;
418
419   if (sysconf (_SC_PAGESIZE) > tramp_globals.map_size)
420     return 0;
421
422   if (ffi_tramp_init_os ())
423     {
424       tramp_globals.status = TRAMP_GLOBALS_PASSED;
425       return 1;
426     }
427
428   tramp_globals.status = TRAMP_GLOBALS_FAILED;
429   return 0;
430 }
431
432 /* ---------------------- Trampoline Table functions ---------------------- */
433
434 /* This code assumes that malloc () is available on all OSes. */
435
436 static void tramp_add (struct tramp *tramp);
437
438 /*
439  * Allocate and initialize a trampoline table.
440  */
441 static int
442 tramp_table_alloc (void)
443 {
444   struct tramp_table *table;
445   struct tramp *tramp_array, *tramp;
446   size_t size;
447   char *code, *parm;
448   int i;
449
450   /*
451    * If we already have tables with free trampolines, there is no need to
452    * allocate a new table.
453    */
454   if (tramp_globals.nfree_tables > 0)
455     return 1;
456
457   /*
458    * Allocate a new trampoline table structure.
459    */
460   table = malloc (sizeof (*table));
461   if (table == NULL)
462     return 0;
463
464   /*
465    * Allocate new trampoline structures.
466    */
467   tramp_array = malloc (sizeof (*tramp) * tramp_globals.ntramp);
468   if (tramp_array == NULL)
469     goto free_table;
470
471   /*
472    * Map a code table and a parameter table into the caller's address space.
473    */
474   if (!tramp_table_map (table))
475     {
476       /*
477        * Failed to map the code and parameter tables.
478        */
479       goto free_tramp_array;
480     }
481
482   /*
483    * Initialize the trampoline table.
484    */
485   table->array = tramp_array;
486   table->free = NULL;
487   table->nfree = 0;
488
489   /*
490    * Populate the trampoline table free list. This will also add the trampoline
491    * table to the global list of trampoline tables.
492    */
493   size = tramp_globals.size;
494   code = table->code_table;
495   parm = table->parm_table;
496   for (i = 0; i < tramp_globals.ntramp; i++)
497     {
498       tramp = &tramp_array[i];
499       tramp->table = table;
500       tramp->code = code;
501       tramp->parm = (struct tramp_parm *) parm;
502       tramp_add (tramp);
503
504       code += size;
505       parm += size;
506     }
507   /* Success */
508   return 1;
509
510 /* Failure */
511 free_tramp_array:
512   free (tramp_array);
513 free_table:
514   free (table);
515   return 0;
516 }
517
518 /*
519  * Free a trampoline table.
520  */
521 static void
522 tramp_table_free (struct tramp_table *table)
523 {
524   tramp_table_unmap (table);
525   free (table->array);
526   free (table);
527 }
528
529 /*
530  * Add a new trampoline table to the global table list.
531  */
532 static void
533 tramp_table_add (struct tramp_table *table)
534 {
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++;
541 }
542
543 /*
544  * Delete a trampoline table from the global table list.
545  */
546 static void
547 tramp_table_del (struct tramp_table *table)
548 {
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;
556 }
557
558 /* ------------------------- Trampoline functions ------------------------- */
559
560 /*
561  * Add a trampoline to its trampoline table.
562  */
563 static void
564 tramp_add (struct tramp *tramp)
565 {
566   struct tramp_table *table = tramp->table;
567
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++;
574
575   if (table->nfree == 1)
576     tramp_table_add (table);
577
578   /*
579    * We don't want to keep too many free trampoline tables lying around.
580    */
581   if (table->nfree == tramp_globals.ntramp &&
582     tramp_globals.nfree_tables > 1)
583     {
584       tramp_table_del (table);
585       tramp_table_free (table);
586     }
587 }
588
589 /*
590  * Remove a trampoline from its trampoline table.
591  */
592 static void
593 tramp_del (struct tramp *tramp)
594 {
595   struct tramp_table *table = tramp->table;
596
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;
604
605   if (table->nfree == 0)
606     tramp_table_del (table);
607 }
608
609 /* ------------------------ Trampoline API functions ------------------------ */
610
611 int
612 ffi_tramp_is_supported(void)
613 {
614   int ret;
615
616   ffi_tramp_lock();
617   ret = ffi_tramp_init ();
618   ffi_tramp_unlock();
619   return ret;
620 }
621
622 /*
623  * Allocate a trampoline and return its opaque address.
624  */
625 void *
626 ffi_tramp_alloc (int flags)
627 {
628   struct tramp *tramp;
629
630   ffi_tramp_lock();
631
632   if (!ffi_tramp_init () || flags != 0)
633     {
634       ffi_tramp_unlock();
635       return NULL;
636     }
637
638   if (!tramp_table_alloc ())
639     {
640       ffi_tramp_unlock();
641       return NULL;
642     }
643
644   tramp = tramp_globals.free_tables->free;
645   tramp_del (tramp);
646
647   ffi_tramp_unlock();
648
649   return tramp;
650 }
651
652 /*
653  * Set the parameters for a trampoline.
654  */
655 void
656 ffi_tramp_set_parms (void *arg, void *target, void *data)
657 {
658   struct tramp *tramp = arg;
659
660   ffi_tramp_lock();
661   tramp->parm->target = target;
662   tramp->parm->data = data;
663   ffi_tramp_unlock();
664 }
665
666 /*
667  * Get the invocation address of a trampoline.
668  */
669 void *
670 ffi_tramp_get_addr (void *arg)
671 {
672   struct tramp *tramp = arg;
673   void *addr;
674
675   ffi_tramp_lock();
676   addr = tramp->code;
677   ffi_tramp_unlock();
678
679   return addr;
680 }
681
682 /*
683  * Free a trampoline.
684  */
685 void
686 ffi_tramp_free (void *arg)
687 {
688   struct tramp *tramp = arg;
689
690   ffi_tramp_lock();
691   tramp_add (tramp);
692   ffi_tramp_unlock();
693 }
694
695 /* ------------------------------------------------------------------------- */
696
697 #else /* !FFI_EXEC_STATIC_TRAMP */
698
699 #include <stddef.h>
700
701 int
702 ffi_tramp_is_supported(void)
703 {
704   return 0;
705 }
706
707 void *
708 ffi_tramp_alloc (int flags)
709 {
710   return NULL;
711 }
712
713 void
714 ffi_tramp_set_parms (void *arg, void *target, void *data)
715 {
716 }
717
718 void *
719 ffi_tramp_get_addr (void *arg)
720 {
721   return NULL;
722 }
723
724 void
725 ffi_tramp_free (void *arg)
726 {
727 }
728
729 #endif /* FFI_EXEC_STATIC_TRAMP */