Coverage Report

Created: 2025-11-16 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libffi/src/tramp.c
Line
Count
Source
1
/* -----------------------------------------------------------------------
2
   tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman
3
             Copyright (c) 2022 Anthony Green
4
5
   API and support functions for managing statically defined closure
6
   trampolines.
7
8
   Permission is hereby granted, free of charge, to any person obtaining
9
   a copy of this software and associated documentation files (the
10
   ``Software''), to deal in the Software without restriction, including
11
   without limitation the rights to use, copy, modify, merge, publish,
12
   distribute, sublicense, and/or sell copies of the Software, and to
13
   permit persons to whom the Software is furnished to do so, subject to
14
   the following conditions:
15
16
   The above copyright notice and this permission notice shall be included
17
   in all copies or substantial portions of the Software.
18
19
   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
20
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23
   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24
   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
   DEALINGS IN THE SOFTWARE.
27
   ----------------------------------------------------------------------- */
28
29
#include <fficonfig.h>
30
31
#ifdef FFI_EXEC_STATIC_TRAMP
32
33
/* -------------------------- Headers and Definitions ---------------------*/
34
/*
35
 * Add support for other OSes later. For now, it is just Linux and Cygwin.
36
 */
37
38
#if defined (__linux__) || defined (__CYGWIN__)
39
#ifdef __linux__
40
#define _GNU_SOURCE 1
41
#endif
42
43
#include <ffi.h>
44
#include <ffi_common.h>
45
46
#include <stdio.h>
47
#include <unistd.h>
48
#include <stdlib.h>
49
#include <stdint.h>
50
#include <fcntl.h>
51
#include <pthread.h>
52
#include <sys/mman.h>
53
#include <tramp.h>
54
#ifdef __linux__
55
#include <linux/limits.h>
56
#include <linux/types.h>
57
#endif
58
#ifdef __CYGWIN__
59
#include <limits.h>
60
#endif
61
#endif
62
63
/*
64
 * Each architecture defines static code for a trampoline code table. The
65
 * trampoline code table is mapped into the address space of a process.
66
 *
67
 * The following architecture specific function returns:
68
 *
69
 *  - the address of the trampoline code table in the text segment
70
 *  - the size of each trampoline in the trampoline code table
71
 *  - the size of the mapping for the whole trampoline code table
72
 */
73
void __attribute__((weak)) *ffi_tramp_arch (size_t *tramp_size,
74
  size_t *map_size);
75
76
/* ------------------------- Trampoline Data Structures --------------------*/
77
78
struct tramp;
79
80
/*
81
 * Trampoline table. Manages one trampoline code table and one trampoline
82
 * parameter table.
83
 *
84
 * prev, next Links in the global trampoline table list.
85
 * code_table Trampoline code table mapping.
86
 * parm_table Trampoline parameter table mapping.
87
 * array  Array of trampolines malloced.
88
 * free   List of free trampolines.
89
 * nfree  Number of free trampolines.
90
 */
91
struct tramp_table
92
{
93
  struct tramp_table *prev;
94
  struct tramp_table *next;
95
  void *code_table;
96
  void *parm_table;
97
  struct tramp *array;
98
  struct tramp *free;
99
  int nfree;
100
};
101
102
/*
103
 * Parameters for each trampoline.
104
 *
105
 * data
106
 *  Data for the target code that the trampoline jumps to.
107
 * target
108
 *  Target code that the trampoline jumps to.
109
 */
110
struct tramp_parm
111
{
112
  void *data;
113
  void *target;
114
};
115
116
/*
117
 * Trampoline structure for each trampoline.
118
 *
119
 * prev, next Links in the trampoline free list of a trampoline table.
120
 * table  Trampoline table to which this trampoline belongs.
121
 * code   Address of this trampoline in the code table mapping.
122
 * parm   Address of this trampoline's parameters in the parameter
123
 *    table mapping.
124
 */
125
struct tramp
126
{
127
  struct tramp *prev;
128
  struct tramp *next;
129
  struct tramp_table *table;
130
  void *code;
131
  struct tramp_parm *parm;
132
};
133
134
enum tramp_globals_status {
135
  TRAMP_GLOBALS_UNINITIALIZED = 0,
136
  TRAMP_GLOBALS_PASSED,
137
  TRAMP_GLOBALS_FAILED,
138
};
139
140
/*
141
 * Trampoline globals.
142
 *
143
 * fd
144
 *  File descriptor of binary file that contains the trampoline code table.
145
 * offset
146
 *  Offset of the trampoline code table in that file.
147
 * text
148
 *  Address of the trampoline code table in the text segment.
149
 * map_size
150
 *  Size of the trampoline code table mapping.
151
 * size
152
 *  Size of one trampoline in the trampoline code table.
153
 * ntramp
154
 *  Total number of trampolines in the trampoline code table.
155
 * free_tables
156
 *  List of trampoline tables that contain free trampolines.
157
 * nfree_tables
158
 *  Number of trampoline tables that contain free trampolines.
159
 * status
160
 *  Initialization status.
161
 */
162
struct tramp_globals
163
{
164
  int fd;
165
  off_t offset;
166
  void *text;
167
  size_t map_size;
168
  size_t size;
169
  int ntramp;
170
  struct tramp_table *free_tables;
171
  int nfree_tables;
172
  enum tramp_globals_status status;
173
};
174
175
static struct tramp_globals tramp_globals;
176
177
/* --------------------- Trampoline File Initialization --------------------*/
178
179
/*
180
 * The trampoline file is the file used to map the trampoline code table into
181
 * the address space of a process. There are two ways to get this file:
182
 *
183
 * - From the OS. E.g., on Linux, /proc/<pid>/maps lists all the memory
184
 *   mappings for <pid>. For file-backed mappings, maps supplies the file name
185
 *   and the file offset. Using this, we can locate the mapping that maps
186
 *   libffi and get the path to the libffi binary. And, we can compute the
187
 *   offset of the trampoline code table within that binary.
188
 *
189
 * - Else, if we can create a temporary file, we can write the trampoline code
190
 *   table from the text segment into the temporary file.
191
 *
192
 * The first method is the preferred one. If the OS security subsystem
193
 * disallows mapping unsigned files with PROT_EXEC, then the second method
194
 * will fail.
195
 *
196
 * If an OS allows the trampoline code table in the text segment to be
197
 * directly remapped (e.g., MACH vm_remap ()), then we don't need the
198
 * trampoline file.
199
 */
200
static int tramp_table_alloc (void);
201
202
#if defined (__linux__) || defined (__CYGWIN__)
203
204
static int
205
ffi_tramp_get_libffi (void)
206
0
{
207
0
  FILE *fp;
208
0
  char file[PATH_MAX], line[PATH_MAX+100], perm[10], dev[10];
209
0
  unsigned long start, end, offset, inode;
210
0
  uintptr_t addr = (uintptr_t) tramp_globals.text;
211
0
  int nfields, found;
212
0
  int open_flags = O_RDONLY;
213
214
0
#ifdef O_CLOEXEC
215
0
  open_flags |= O_CLOEXEC;
216
0
#endif
217
218
0
  snprintf (file, PATH_MAX, "/proc/%d/maps", getpid());
219
0
  fp = fopen (file, "r");
220
0
  if (fp == NULL)
221
0
    return 0;
222
223
0
  found = 0;
224
0
  while (feof (fp) == 0) {
225
0
    if (fgets (line, sizeof (line), fp) == 0)
226
0
      break;
227
228
0
    nfields = sscanf (line, "%lx-%lx %9s %lx %9s %ld %s",
229
0
      &start, &end, perm, &offset, dev, &inode, file);
230
0
    if (nfields != 7)
231
0
      continue;
232
233
0
    if (addr >= start && addr < end) {
234
0
      tramp_globals.offset = offset + (addr - start);
235
0
      found = 1;
236
0
      break;
237
0
    }
238
0
  }
239
0
  fclose (fp);
240
241
0
  if (!found)
242
0
    return 0;
243
244
0
  tramp_globals.fd = open (file, open_flags);
245
0
  if (tramp_globals.fd == -1)
246
0
    return 0;
247
248
  /*
249
   * Allocate a trampoline table just to make sure that the trampoline code
250
   * table can be mapped.
251
   */
252
0
  if (!tramp_table_alloc ())
253
0
    {
254
0
      close (tramp_globals.fd);
255
0
      tramp_globals.fd = -1;
256
0
      return 0;
257
0
    }
258
0
  return 1;
259
0
}
260
261
#endif /* defined (__linux__) || defined (__CYGWIN__) */
262
263
#if defined (__linux__) || defined (__CYGWIN__)
264
265
static int
266
ffi_tramp_get_temp_file (void)
267
0
{
268
0
  ssize_t count;
269
270
0
  tramp_globals.offset = 0;
271
0
  tramp_globals.fd = open_temp_exec_file ();
272
273
  /*
274
   * Write the trampoline code table into the temporary file and allocate a
275
   * trampoline table to make sure that the temporary file can be mapped.
276
   */
277
0
  count = write(tramp_globals.fd, tramp_globals.text, tramp_globals.map_size);
278
0
  if (count >=0 && (size_t)count == tramp_globals.map_size && tramp_table_alloc ())
279
0
    return 1;
280
281
0
  close (tramp_globals.fd);
282
0
  tramp_globals.fd = -1;
283
0
  return 0;
284
0
}
285
286
#endif /* defined (__linux__) || defined (__CYGWIN__) */
287
288
/* ------------------------ OS-specific Initialization ----------------------*/
289
290
#if defined (__linux__) || defined (__CYGWIN__)
291
292
static int
293
ffi_tramp_init_os (void)
294
0
{
295
0
  if (ffi_tramp_get_libffi ())
296
0
    return 1;
297
0
  return ffi_tramp_get_temp_file ();
298
0
}
299
300
#endif /* defined (__linux__) || defined (__CYGWIN__) */
301
302
/* --------------------------- OS-specific Locking -------------------------*/
303
304
#if defined (__linux__) || defined (__CYGWIN__)
305
306
static pthread_mutex_t tramp_globals_mutex = PTHREAD_MUTEX_INITIALIZER;
307
308
static void
309
ffi_tramp_lock(void)
310
0
{
311
0
  pthread_mutex_lock (&tramp_globals_mutex);
312
0
}
313
314
static void
315
ffi_tramp_unlock(void)
316
0
{
317
0
  pthread_mutex_unlock (&tramp_globals_mutex);
318
0
}
319
320
#endif /* defined (__linux__) || defined (__CYGWIN__) */
321
322
/* ------------------------ OS-specific Memory Mapping ----------------------*/
323
324
/*
325
 * Create a trampoline code table mapping and a trampoline parameter table
326
 * mapping. The two mappings must be adjacent to each other for PC-relative
327
 * access.
328
 *
329
 * For each trampoline in the code table, there is a corresponding parameter
330
 * block in the parameter table. The size of the parameter block is the same
331
 * as the size of the trampoline. This means that the parameter block is at
332
 * a fixed offset from its trampoline making it easy for a trampoline to find
333
 * its parameters using PC-relative access.
334
 *
335
 * The parameter block will contain a struct tramp_parm. This means that
336
 * sizeof (struct tramp_parm) cannot exceed the size of a parameter block.
337
 */
338
339
#if defined (__linux__) || defined (__CYGWIN__)
340
341
static int
342
tramp_table_map (struct tramp_table *table)
343
0
{
344
0
  char *addr;
345
346
  /*
347
   * Create an anonymous mapping twice the map size. The top half will be used
348
   * for the code table. The bottom half will be used for the parameter table.
349
   */
350
0
  addr = mmap (NULL, tramp_globals.map_size * 2, PROT_READ | PROT_WRITE,
351
0
    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
352
0
  if (addr == MAP_FAILED)
353
0
    return 0;
354
355
  /*
356
   * Replace the top half of the anonymous mapping with the code table mapping.
357
   */
358
0
  table->code_table = mmap (addr, tramp_globals.map_size, PROT_READ | PROT_EXEC,
359
0
    MAP_PRIVATE | MAP_FIXED, tramp_globals.fd, tramp_globals.offset);
360
0
  if (table->code_table == MAP_FAILED)
361
0
    {
362
0
      (void) munmap (addr, tramp_globals.map_size * 2);
363
0
      return 0;
364
0
    }
365
0
  table->parm_table = table->code_table + tramp_globals.map_size;
366
0
  return 1;
367
0
}
368
369
static void
370
tramp_table_unmap (struct tramp_table *table)
371
0
{
372
0
  (void) munmap (table->code_table, tramp_globals.map_size);
373
0
  (void) munmap (table->parm_table, tramp_globals.map_size);
374
0
}
375
376
#endif /* defined (__linux__) || defined (__CYGWIN__) */
377
378
/* ------------------------ Trampoline Initialization ----------------------*/
379
380
/*
381
 * Initialize the static trampoline feature.
382
 */
383
static int
384
ffi_tramp_init (void)
385
0
{
386
0
  long page_size;
387
388
0
  if (tramp_globals.status == TRAMP_GLOBALS_PASSED)
389
0
    return 1;
390
391
0
  if (tramp_globals.status == TRAMP_GLOBALS_FAILED)
392
0
    return 0;
393
394
0
  if (ffi_tramp_arch == NULL)
395
0
    {
396
0
      tramp_globals.status = TRAMP_GLOBALS_FAILED;
397
0
      return 0;
398
0
    }
399
400
0
  tramp_globals.free_tables = NULL;
401
0
  tramp_globals.nfree_tables = 0;
402
403
  /*
404
   * Get trampoline code table information from the architecture.
405
   */
406
0
  tramp_globals.text = ffi_tramp_arch (&tramp_globals.size,
407
0
    &tramp_globals.map_size);
408
0
  tramp_globals.ntramp = tramp_globals.map_size / tramp_globals.size;
409
410
0
  page_size = sysconf (_SC_PAGESIZE);
411
0
  if (page_size >= 0 && (size_t)page_size > tramp_globals.map_size)
412
0
    return 0;
413
414
0
  if (ffi_tramp_init_os ())
415
0
    {
416
0
      tramp_globals.status = TRAMP_GLOBALS_PASSED;
417
0
      return 1;
418
0
    }
419
420
0
  tramp_globals.status = TRAMP_GLOBALS_FAILED;
421
0
  return 0;
422
0
}
423
424
/* ---------------------- Trampoline Table functions ---------------------- */
425
426
/* This code assumes that malloc () is available on all OSes. */
427
428
static void tramp_add (struct tramp *tramp);
429
430
/*
431
 * Allocate and initialize a trampoline table.
432
 */
433
static int
434
tramp_table_alloc (void)
435
0
{
436
0
  struct tramp_table *table;
437
0
  struct tramp *tramp_array, *tramp;
438
0
  size_t size;
439
0
  char *code, *parm;
440
0
  int i;
441
442
  /*
443
   * If we already have tables with free trampolines, there is no need to
444
   * allocate a new table.
445
   */
446
0
  if (tramp_globals.nfree_tables > 0)
447
0
    return 1;
448
449
  /*
450
   * Allocate a new trampoline table structure.
451
   */
452
0
  table = malloc (sizeof (*table));
453
0
  if (table == NULL)
454
0
    return 0;
455
456
  /*
457
   * Allocate new trampoline structures.
458
   */
459
0
  tramp_array = malloc (sizeof (*tramp) * tramp_globals.ntramp);
460
0
  if (tramp_array == NULL)
461
0
    goto free_table;
462
463
  /*
464
   * Map a code table and a parameter table into the caller's address space.
465
   */
466
0
  if (!tramp_table_map (table))
467
0
    {
468
      /*
469
       * Failed to map the code and parameter tables.
470
       */
471
0
      goto free_tramp_array;
472
0
    }
473
474
  /*
475
   * Initialize the trampoline table.
476
   */
477
0
  table->array = tramp_array;
478
0
  table->free = NULL;
479
0
  table->nfree = 0;
480
481
  /*
482
   * Populate the trampoline table free list. This will also add the trampoline
483
   * table to the global list of trampoline tables.
484
   */
485
0
  size = tramp_globals.size;
486
0
  code = table->code_table;
487
0
  parm = table->parm_table;
488
0
  for (i = 0; i < tramp_globals.ntramp; i++)
489
0
    {
490
0
      tramp = &tramp_array[i];
491
0
      tramp->table = table;
492
0
      tramp->code = code;
493
0
      tramp->parm = (struct tramp_parm *) parm;
494
0
      tramp_add (tramp);
495
496
0
      code += size;
497
0
      parm += size;
498
0
    }
499
  /* Success */
500
0
  return 1;
501
502
/* Failure */
503
0
free_tramp_array:
504
0
  free (tramp_array);
505
0
free_table:
506
0
  free (table);
507
0
  return 0;
508
0
}
509
510
/*
511
 * Free a trampoline table.
512
 */
513
static void
514
tramp_table_free (struct tramp_table *table)
515
0
{
516
0
  tramp_table_unmap (table);
517
0
  free (table->array);
518
0
  free (table);
519
0
}
520
521
/*
522
 * Add a new trampoline table to the global table list.
523
 */
524
static void
525
tramp_table_add (struct tramp_table *table)
526
0
{
527
0
  table->next = tramp_globals.free_tables;
528
0
  table->prev = NULL;
529
0
  if (tramp_globals.free_tables != NULL)
530
0
    tramp_globals.free_tables->prev = table;
531
0
  tramp_globals.free_tables = table;
532
0
  tramp_globals.nfree_tables++;
533
0
}
534
535
/*
536
 * Delete a trampoline table from the global table list.
537
 */
538
static void
539
tramp_table_del (struct tramp_table *table)
540
0
{
541
0
  tramp_globals.nfree_tables--;
542
0
  if (table->prev != NULL)
543
0
    table->prev->next = table->next;
544
0
  if (table->next != NULL)
545
0
    table->next->prev = table->prev;
546
0
  if (tramp_globals.free_tables == table)
547
0
    tramp_globals.free_tables = table->next;
548
0
}
549
550
/* ------------------------- Trampoline functions ------------------------- */
551
552
/*
553
 * Add a trampoline to its trampoline table.
554
 */
555
static void
556
tramp_add (struct tramp *tramp)
557
0
{
558
0
  struct tramp_table *table = tramp->table;
559
560
0
  tramp->next = table->free;
561
0
  tramp->prev = NULL;
562
0
  if (table->free != NULL)
563
0
    table->free->prev = tramp;
564
0
  table->free = tramp;
565
0
  table->nfree++;
566
567
0
  if (table->nfree == 1)
568
0
    tramp_table_add (table);
569
570
  /*
571
   * We don't want to keep too many free trampoline tables lying around.
572
   */
573
0
  if (table->nfree == tramp_globals.ntramp &&
574
0
    tramp_globals.nfree_tables > 1)
575
0
    {
576
0
      tramp_table_del (table);
577
0
      tramp_table_free (table);
578
0
    }
579
0
}
580
581
/*
582
 * Remove a trampoline from its trampoline table.
583
 */
584
static void
585
tramp_del (struct tramp *tramp)
586
0
{
587
0
  struct tramp_table *table = tramp->table;
588
589
0
  table->nfree--;
590
0
  if (tramp->prev != NULL)
591
0
    tramp->prev->next = tramp->next;
592
0
  if (tramp->next != NULL)
593
0
    tramp->next->prev = tramp->prev;
594
0
  if (table->free == tramp)
595
0
    table->free = tramp->next;
596
597
0
  if (table->nfree == 0)
598
0
    tramp_table_del (table);
599
0
}
600
601
/* ------------------------ Trampoline API functions ------------------------ */
602
603
int
604
ffi_tramp_is_supported(void)
605
0
{
606
0
  int ret;
607
608
0
  ffi_tramp_lock();
609
0
  ret = ffi_tramp_init ();
610
0
  ffi_tramp_unlock();
611
0
  return ret;
612
0
}
613
614
/*
615
 * Allocate a trampoline and return its opaque address.
616
 */
617
void *
618
ffi_tramp_alloc (int flags)
619
0
{
620
0
  struct tramp *tramp;
621
622
0
  ffi_tramp_lock();
623
624
0
  if (!ffi_tramp_init () || flags != 0)
625
0
    {
626
0
      ffi_tramp_unlock();
627
0
      return NULL;
628
0
    }
629
630
0
  if (!tramp_table_alloc ())
631
0
    {
632
0
      ffi_tramp_unlock();
633
0
      return NULL;
634
0
    }
635
636
0
  tramp = tramp_globals.free_tables->free;
637
0
  tramp_del (tramp);
638
639
0
  ffi_tramp_unlock();
640
641
0
  return tramp;
642
0
}
643
644
/*
645
 * Set the parameters for a trampoline.
646
 */
647
void
648
ffi_tramp_set_parms (void *arg, void *target, void *data)
649
0
{
650
0
  struct tramp *tramp = arg;
651
652
0
  ffi_tramp_lock();
653
0
  tramp->parm->target = target;
654
0
  tramp->parm->data = data;
655
0
  ffi_tramp_unlock();
656
0
}
657
658
/*
659
 * Get the invocation address of a trampoline.
660
 */
661
void *
662
ffi_tramp_get_addr (void *arg)
663
0
{
664
0
  struct tramp *tramp = arg;
665
0
  void *addr;
666
667
0
  ffi_tramp_lock();
668
0
  addr = tramp->code;
669
0
  ffi_tramp_unlock();
670
671
0
  return addr;
672
0
}
673
674
/*
675
 * Free a trampoline.
676
 */
677
void
678
ffi_tramp_free (void *arg)
679
0
{
680
0
  struct tramp *tramp = arg;
681
682
0
  ffi_tramp_lock();
683
0
  tramp_add (tramp);
684
0
  ffi_tramp_unlock();
685
0
}
686
687
/* ------------------------------------------------------------------------- */
688
689
#else /* !FFI_EXEC_STATIC_TRAMP */
690
691
#include <stddef.h>
692
693
int
694
ffi_tramp_is_supported(void)
695
{
696
  return 0;
697
}
698
699
void *
700
ffi_tramp_alloc (int flags)
701
{
702
  return NULL;
703
}
704
705
void
706
ffi_tramp_set_parms (void *arg, void *target, void *data)
707
{
708
}
709
710
void *
711
ffi_tramp_get_addr (void *arg)
712
{
713
  return NULL;
714
}
715
716
void
717
ffi_tramp_free (void *arg)
718
{
719
}
720
721
#endif /* FFI_EXEC_STATIC_TRAMP */